rnd-20080125-2-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 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62
63 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
64
65 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
66
67 #if USE_DELAYED_GFX_REDRAW
68 #define TEST_DrawLevelField(x, y)                               \
69         GfxRedraw[x][y] |= GFX_REDRAW_TILE
70 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
71         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
72 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
73         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
74 #define TEST_DrawTwinkleOnField(x, y)                           \
75         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
76 #else
77 #define TEST_DrawLevelField(x, y)                               \
78              DrawLevelField(x, y)
79 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
80              DrawLevelFieldCrumbledSand(x, y)
81 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
82              DrawLevelFieldCrumbledSandNeighbours(x, y)
83 #define TEST_DrawTwinkleOnField(x, y)                           \
84              DrawTwinkleOnField(x, y)
85 #endif
86
87
88 /* for DigField() */
89 #define DF_NO_PUSH              0
90 #define DF_DIG                  1
91 #define DF_SNAP                 2
92
93 /* for MovePlayer() */
94 #define MP_NO_ACTION            0
95 #define MP_MOVING               1
96 #define MP_ACTION               2
97 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
98
99 /* for ScrollPlayer() */
100 #define SCROLL_INIT             0
101 #define SCROLL_GO_ON            1
102
103 /* for Bang()/Explode() */
104 #define EX_PHASE_START          0
105 #define EX_TYPE_NONE            0
106 #define EX_TYPE_NORMAL          (1 << 0)
107 #define EX_TYPE_CENTER          (1 << 1)
108 #define EX_TYPE_BORDER          (1 << 2)
109 #define EX_TYPE_CROSS           (1 << 3)
110 #define EX_TYPE_DYNA            (1 << 4)
111 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
112
113 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
114 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
115 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
116 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
117
118 /* special positions in the game control window (relative to control window) */
119 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
120 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
121 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
122 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
123 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
124 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
125 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
126 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
127 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
128 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
129 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
130 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
131 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
132 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
133 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
134 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
135
136 /* special positions in the game control window (relative to main window) */
137 #define DX_LEVEL1               (DX + XX_LEVEL1)
138 #define DX_LEVEL2               (DX + XX_LEVEL2)
139 #define DX_LEVEL                (DX + XX_LEVEL)
140 #define DY_LEVEL                (DY + YY_LEVEL)
141 #define DX_EMERALDS             (DX + XX_EMERALDS)
142 #define DY_EMERALDS             (DY + YY_EMERALDS)
143 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
144 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
145 #define DX_KEYS                 (DX + XX_KEYS)
146 #define DY_KEYS                 (DY + YY_KEYS)
147 #define DX_SCORE                (DX + XX_SCORE)
148 #define DY_SCORE                (DY + YY_SCORE)
149 #define DX_TIME1                (DX + XX_TIME1)
150 #define DX_TIME2                (DX + XX_TIME2)
151 #define DX_TIME                 (DX + XX_TIME)
152 #define DY_TIME                 (DY + YY_TIME)
153
154 #if 1
155 /* game panel display and control definitions */
156
157 #define GAME_PANEL_LEVEL_NUMBER                 0
158 #define GAME_PANEL_GEMS                         1
159 #define GAME_PANEL_INVENTORY_COUNT              2
160 #define GAME_PANEL_INVENTORY_FIRST_1            3
161 #define GAME_PANEL_INVENTORY_FIRST_2            4
162 #define GAME_PANEL_INVENTORY_FIRST_3            5
163 #define GAME_PANEL_INVENTORY_FIRST_4            6
164 #define GAME_PANEL_INVENTORY_FIRST_5            7
165 #define GAME_PANEL_INVENTORY_FIRST_6            8
166 #define GAME_PANEL_INVENTORY_FIRST_7            9
167 #define GAME_PANEL_INVENTORY_FIRST_8            10
168 #define GAME_PANEL_INVENTORY_LAST_1             11
169 #define GAME_PANEL_INVENTORY_LAST_2             12
170 #define GAME_PANEL_INVENTORY_LAST_3             13
171 #define GAME_PANEL_INVENTORY_LAST_4             14
172 #define GAME_PANEL_INVENTORY_LAST_5             15
173 #define GAME_PANEL_INVENTORY_LAST_6             16
174 #define GAME_PANEL_INVENTORY_LAST_7             17
175 #define GAME_PANEL_INVENTORY_LAST_8             18
176 #define GAME_PANEL_KEY_1                        19
177 #define GAME_PANEL_KEY_2                        20
178 #define GAME_PANEL_KEY_3                        21
179 #define GAME_PANEL_KEY_4                        22
180 #define GAME_PANEL_KEY_5                        23
181 #define GAME_PANEL_KEY_6                        24
182 #define GAME_PANEL_KEY_7                        25
183 #define GAME_PANEL_KEY_8                        26
184 #define GAME_PANEL_KEY_WHITE                    27
185 #define GAME_PANEL_KEY_WHITE_COUNT              28
186 #define GAME_PANEL_SCORE                        29
187 #define GAME_PANEL_HIGHSCORE                    30
188 #define GAME_PANEL_TIME                         31
189 #define GAME_PANEL_TIME_HH                      32
190 #define GAME_PANEL_TIME_MM                      33
191 #define GAME_PANEL_TIME_SS                      34
192 #define GAME_PANEL_SHIELD_NORMAL                35
193 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
194 #define GAME_PANEL_SHIELD_DEADLY                37
195 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
196 #define GAME_PANEL_EXIT                         39
197 #define GAME_PANEL_EMC_MAGIC_BALL               40
198 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
199 #define GAME_PANEL_LIGHT_SWITCH                 42
200 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
201 #define GAME_PANEL_TIMEGATE_SWITCH              44
202 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
203 #define GAME_PANEL_SWITCHGATE_SWITCH            46
204 #define GAME_PANEL_EMC_LENSES                   47
205 #define GAME_PANEL_EMC_LENSES_TIME              48
206 #define GAME_PANEL_EMC_MAGNIFIER                49
207 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
208 #define GAME_PANEL_BALLOON_SWITCH               51
209 #define GAME_PANEL_DYNABOMB_NUMBER              52
210 #define GAME_PANEL_DYNABOMB_SIZE                53
211 #define GAME_PANEL_DYNABOMB_POWER               54
212 #define GAME_PANEL_PENGUINS                     55
213 #define GAME_PANEL_SOKOBAN_OBJECTS              56
214 #define GAME_PANEL_SOKOBAN_FIELDS               57
215 #define GAME_PANEL_ROBOT_WHEEL                  58
216 #define GAME_PANEL_CONVEYOR_BELT_1              59
217 #define GAME_PANEL_CONVEYOR_BELT_2              60
218 #define GAME_PANEL_CONVEYOR_BELT_3              61
219 #define GAME_PANEL_CONVEYOR_BELT_4              62
220 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
221 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
222 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
223 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
224 #define GAME_PANEL_MAGIC_WALL                   67
225 #define GAME_PANEL_MAGIC_WALL_TIME              68
226 #define GAME_PANEL_GRAVITY_STATE                69
227 #define GAME_PANEL_GRAPHIC_1                    70
228 #define GAME_PANEL_GRAPHIC_2                    71
229 #define GAME_PANEL_GRAPHIC_3                    72
230 #define GAME_PANEL_GRAPHIC_4                    73
231 #define GAME_PANEL_GRAPHIC_5                    74
232 #define GAME_PANEL_GRAPHIC_6                    75
233 #define GAME_PANEL_GRAPHIC_7                    76
234 #define GAME_PANEL_GRAPHIC_8                    77
235 #define GAME_PANEL_ELEMENT_1                    78
236 #define GAME_PANEL_ELEMENT_2                    79
237 #define GAME_PANEL_ELEMENT_3                    80
238 #define GAME_PANEL_ELEMENT_4                    81
239 #define GAME_PANEL_ELEMENT_5                    82
240 #define GAME_PANEL_ELEMENT_6                    83
241 #define GAME_PANEL_ELEMENT_7                    84
242 #define GAME_PANEL_ELEMENT_8                    85
243 #define GAME_PANEL_ELEMENT_COUNT_1              86
244 #define GAME_PANEL_ELEMENT_COUNT_2              87
245 #define GAME_PANEL_ELEMENT_COUNT_3              88
246 #define GAME_PANEL_ELEMENT_COUNT_4              89
247 #define GAME_PANEL_ELEMENT_COUNT_5              90
248 #define GAME_PANEL_ELEMENT_COUNT_6              91
249 #define GAME_PANEL_ELEMENT_COUNT_7              92
250 #define GAME_PANEL_ELEMENT_COUNT_8              93
251 #define GAME_PANEL_CE_SCORE_1                   94
252 #define GAME_PANEL_CE_SCORE_2                   95
253 #define GAME_PANEL_CE_SCORE_3                   96
254 #define GAME_PANEL_CE_SCORE_4                   97
255 #define GAME_PANEL_CE_SCORE_5                   98
256 #define GAME_PANEL_CE_SCORE_6                   99
257 #define GAME_PANEL_CE_SCORE_7                   100
258 #define GAME_PANEL_CE_SCORE_8                   101
259 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
260 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
261 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
262 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
263 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
264 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
265 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
266 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
267 #define GAME_PANEL_PLAYER_NAME                  110
268 #define GAME_PANEL_LEVEL_NAME                   111
269 #define GAME_PANEL_LEVEL_AUTHOR                 112
270
271 #define NUM_GAME_PANEL_CONTROLS                 113
272
273 struct GamePanelOrderInfo
274 {
275   int nr;
276   int sort_priority;
277 };
278
279 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
280
281 struct GamePanelControlInfo
282 {
283   int nr;
284
285   struct TextPosInfo *pos;
286   int type;
287
288   int value, last_value;
289   int frame, last_frame;
290   int gfx_frame;
291   int gfx_random;
292 };
293
294 static struct GamePanelControlInfo game_panel_controls[] =
295 {
296   {
297     GAME_PANEL_LEVEL_NUMBER,
298     &game.panel.level_number,
299     TYPE_INTEGER,
300   },
301   {
302     GAME_PANEL_GEMS,
303     &game.panel.gems,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_INVENTORY_COUNT,
308     &game.panel.inventory_count,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_FIRST_1,
313     &game.panel.inventory_first[0],
314     TYPE_ELEMENT,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_2,
318     &game.panel.inventory_first[1],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_3,
323     &game.panel.inventory_first[2],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_4,
328     &game.panel.inventory_first[3],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_5,
333     &game.panel.inventory_first[4],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_6,
338     &game.panel.inventory_first[5],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_7,
343     &game.panel.inventory_first[6],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_8,
348     &game.panel.inventory_first[7],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_LAST_1,
353     &game.panel.inventory_last[0],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_2,
358     &game.panel.inventory_last[1],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_3,
363     &game.panel.inventory_last[2],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_4,
368     &game.panel.inventory_last[3],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_5,
373     &game.panel.inventory_last[4],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_6,
378     &game.panel.inventory_last[5],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_7,
383     &game.panel.inventory_last[6],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_8,
388     &game.panel.inventory_last[7],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_KEY_1,
393     &game.panel.key[0],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_2,
398     &game.panel.key[1],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_3,
403     &game.panel.key[2],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_4,
408     &game.panel.key[3],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_5,
413     &game.panel.key[4],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_6,
418     &game.panel.key[5],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_7,
423     &game.panel.key[6],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_8,
428     &game.panel.key[7],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_WHITE,
433     &game.panel.key_white,
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE_COUNT,
438     &game.panel.key_white_count,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_SCORE,
443     &game.panel.score,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_HIGHSCORE,
448     &game.panel.highscore,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_TIME,
453     &game.panel.time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME_HH,
458     &game.panel.time_hh,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_MM,
463     &game.panel.time_mm,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_SS,
468     &game.panel.time_ss,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_SHIELD_NORMAL,
473     &game.panel.shield_normal,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL_TIME,
478     &game.panel.shield_normal_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_SHIELD_DEADLY,
483     &game.panel.shield_deadly,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY_TIME,
488     &game.panel.shield_deadly_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_EXIT,
493     &game.panel.exit,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_MAGIC_BALL,
498     &game.panel.emc_magic_ball,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
503     &game.panel.emc_magic_ball_switch,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_LIGHT_SWITCH,
508     &game.panel.light_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH_TIME,
513     &game.panel.light_switch_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_TIMEGATE_SWITCH,
518     &game.panel.timegate_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH_TIME,
523     &game.panel.timegate_switch_time,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_SWITCHGATE_SWITCH,
528     &game.panel.switchgate_switch,
529     TYPE_ELEMENT,
530   },
531   {
532     GAME_PANEL_EMC_LENSES,
533     &game.panel.emc_lenses,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES_TIME,
538     &game.panel.emc_lenses_time,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_EMC_MAGNIFIER,
543     &game.panel.emc_magnifier,
544     TYPE_ELEMENT,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER_TIME,
548     &game.panel.emc_magnifier_time,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_BALLOON_SWITCH,
553     &game.panel.balloon_switch,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_DYNABOMB_NUMBER,
558     &game.panel.dynabomb_number,
559     TYPE_INTEGER,
560   },
561   {
562     GAME_PANEL_DYNABOMB_SIZE,
563     &game.panel.dynabomb_size,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_POWER,
568     &game.panel.dynabomb_power,
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_PENGUINS,
573     &game.panel.penguins,
574     TYPE_INTEGER,
575   },
576   {
577     GAME_PANEL_SOKOBAN_OBJECTS,
578     &game.panel.sokoban_objects,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_FIELDS,
583     &game.panel.sokoban_fields,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_ROBOT_WHEEL,
588     &game.panel.robot_wheel,
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_1,
593     &game.panel.conveyor_belt[0],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_2,
598     &game.panel.conveyor_belt[1],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_3,
603     &game.panel.conveyor_belt[2],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_4,
608     &game.panel.conveyor_belt[3],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
613     &game.panel.conveyor_belt_switch[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
618     &game.panel.conveyor_belt_switch[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
623     &game.panel.conveyor_belt_switch[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
628     &game.panel.conveyor_belt_switch[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_MAGIC_WALL,
633     &game.panel.magic_wall,
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL_TIME,
638     &game.panel.magic_wall_time,
639     TYPE_INTEGER,
640   },
641   {
642     GAME_PANEL_GRAVITY_STATE,
643     &game.panel.gravity_state,
644     TYPE_STRING,
645   },
646   {
647     GAME_PANEL_GRAPHIC_1,
648     &game.panel.graphic[0],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_GRAPHIC_2,
653     &game.panel.graphic[1],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_3,
658     &game.panel.graphic[2],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_4,
663     &game.panel.graphic[3],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_5,
668     &game.panel.graphic[4],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_6,
673     &game.panel.graphic[5],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_7,
678     &game.panel.graphic[6],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_8,
683     &game.panel.graphic[7],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_1,
688     &game.panel.element[0],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_2,
693     &game.panel.element[1],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_3,
698     &game.panel.element[2],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_4,
703     &game.panel.element[3],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_5,
708     &game.panel.element[4],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_6,
713     &game.panel.element[5],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_7,
718     &game.panel.element[6],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_8,
723     &game.panel.element[7],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_1,
728     &game.panel.element_count[0],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_2,
733     &game.panel.element_count[1],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_3,
738     &game.panel.element_count[2],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_4,
743     &game.panel.element_count[3],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_5,
748     &game.panel.element_count[4],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_6,
753     &game.panel.element_count[5],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_7,
758     &game.panel.element_count[6],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_8,
763     &game.panel.element_count[7],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_1,
768     &game.panel.ce_score[0],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_2,
773     &game.panel.ce_score[1],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_3,
778     &game.panel.ce_score[2],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_4,
783     &game.panel.ce_score[3],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_5,
788     &game.panel.ce_score[4],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_6,
793     &game.panel.ce_score[5],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_7,
798     &game.panel.ce_score[6],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_8,
803     &game.panel.ce_score[7],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_1_ELEMENT,
808     &game.panel.ce_score_element[0],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_CE_SCORE_2_ELEMENT,
813     &game.panel.ce_score_element[1],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_3_ELEMENT,
818     &game.panel.ce_score_element[2],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_4_ELEMENT,
823     &game.panel.ce_score_element[3],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_5_ELEMENT,
828     &game.panel.ce_score_element[4],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_6_ELEMENT,
833     &game.panel.ce_score_element[5],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_7_ELEMENT,
838     &game.panel.ce_score_element[6],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_8_ELEMENT,
843     &game.panel.ce_score_element[7],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_PLAYER_NAME,
848     &game.panel.player_name,
849     TYPE_STRING,
850   },
851   {
852     GAME_PANEL_LEVEL_NAME,
853     &game.panel.level_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_AUTHOR,
858     &game.panel.level_author,
859     TYPE_STRING,
860   },
861
862   {
863     -1,
864     NULL,
865     -1,
866   }
867 };
868 #endif
869
870
871 /* values for delayed check of falling and moving elements and for collision */
872 #define CHECK_DELAY_MOVING      3
873 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
874 #define CHECK_DELAY_COLLISION   2
875 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
876
877 /* values for initial player move delay (initial delay counter value) */
878 #define INITIAL_MOVE_DELAY_OFF  -1
879 #define INITIAL_MOVE_DELAY_ON   0
880
881 /* values for player movement speed (which is in fact a delay value) */
882 #define MOVE_DELAY_MIN_SPEED    32
883 #define MOVE_DELAY_NORMAL_SPEED 8
884 #define MOVE_DELAY_HIGH_SPEED   4
885 #define MOVE_DELAY_MAX_SPEED    1
886
887 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
888 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
889
890 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
891 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
892
893 /* values for other actions */
894 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
895 #define MOVE_STEPSIZE_MIN       (1)
896 #define MOVE_STEPSIZE_MAX       (TILEX)
897
898 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
899 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
900
901 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
902
903 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
904                                  RND(element_info[e].push_delay_random))
905 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
906                                  RND(element_info[e].drop_delay_random))
907 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
908                                  RND(element_info[e].move_delay_random))
909 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
910                                     (element_info[e].move_delay_random))
911 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
912                                  RND(element_info[e].ce_value_random_initial))
913 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
914 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
915                                  RND((c)->delay_random * (c)->delay_frames))
916 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
917                                  RND((c)->delay_random))
918
919
920 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
921          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
922
923 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
924         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
925          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
926          (be) + (e) - EL_SELF)
927
928 #define GET_PLAYER_FROM_BITS(p)                                         \
929         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
930
931 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
932         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
933          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
934          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
935          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
936          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
937          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
938          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
939          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
940          (e))
941
942 #define CAN_GROW_INTO(e)                                                \
943         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
944
945 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
946                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
947                                         (condition)))
948
949 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
950                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
951                                         (CAN_MOVE_INTO_ACID(e) &&       \
952                                          Feld[x][y] == EL_ACID) ||      \
953                                         (condition)))
954
955 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
956                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Feld[x][y] == EL_ACID) ||      \
959                                         (condition)))
960
961 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
962                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
963                                         (condition) ||                  \
964                                         (CAN_MOVE_INTO_ACID(e) &&       \
965                                          Feld[x][y] == EL_ACID) ||      \
966                                         (DONT_COLLIDE_WITH(e) &&        \
967                                          IS_PLAYER(x, y) &&             \
968                                          !PLAYER_ENEMY_PROTECTED(x, y))))
969
970 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
971         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
972
973 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
975
976 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
978
979 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
980         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
981                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
982
983 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
984         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
985
986 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
987         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
988
989 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
990         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
991
992 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
993         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
994
995 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
996         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
997
998 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
999         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1000                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1001                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1002                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1003                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1004 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1005         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1006
1007 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1008         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1009
1010 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1011         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012
1013 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1014         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1015                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1016
1017 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1018
1019 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1020                 (!IS_PLAYER(x, y) &&                                    \
1021                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1022
1023 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1024         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1025
1026 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1027 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1028
1029 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1030 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1031 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1032 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1033
1034 /* game button identifiers */
1035 #define GAME_CTRL_ID_STOP               0
1036 #define GAME_CTRL_ID_PAUSE              1
1037 #define GAME_CTRL_ID_PLAY               2
1038 #define SOUND_CTRL_ID_MUSIC             3
1039 #define SOUND_CTRL_ID_LOOPS             4
1040 #define SOUND_CTRL_ID_SIMPLE            5
1041
1042 #define NUM_GAME_BUTTONS                6
1043
1044
1045 /* forward declaration for internal use */
1046
1047 static void CreateField(int, int, int);
1048
1049 static void ResetGfxAnimation(int, int);
1050
1051 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1052 static void AdvanceFrameAndPlayerCounters(int);
1053
1054 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1055 static boolean MovePlayer(struct PlayerInfo *, int, int);
1056 static void ScrollPlayer(struct PlayerInfo *, int);
1057 static void ScrollScreen(struct PlayerInfo *, int);
1058
1059 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1060 static boolean DigFieldByCE(int, int, int);
1061 static boolean SnapField(struct PlayerInfo *, int, int);
1062 static boolean DropElement(struct PlayerInfo *);
1063
1064 static void InitBeltMovement(void);
1065 static void CloseAllOpenTimegates(void);
1066 static void CheckGravityMovement(struct PlayerInfo *);
1067 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1068 static void KillPlayerUnlessEnemyProtected(int, int);
1069 static void KillPlayerUnlessExplosionProtected(int, int);
1070
1071 static void TestIfPlayerTouchesCustomElement(int, int);
1072 static void TestIfElementTouchesCustomElement(int, int);
1073 static void TestIfElementHitsCustomElement(int, int, int);
1074 #if 0
1075 static void TestIfElementSmashesCustomElement(int, int, int);
1076 #endif
1077
1078 static void HandleElementChange(int, int, int);
1079 static void ExecuteCustomElementAction(int, int, int, int);
1080 static boolean ChangeElement(int, int, int, int);
1081
1082 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1083 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1084         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1085 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1086         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1087 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1088         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1089 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1090         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1091
1092 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1093 #define CheckElementChange(x, y, e, te, ev)                             \
1094         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1095 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1096         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1097 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1098         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1099
1100 static void PlayLevelSound(int, int, int);
1101 static void PlayLevelSoundNearest(int, int, int);
1102 static void PlayLevelSoundAction(int, int, int);
1103 static void PlayLevelSoundElementAction(int, int, int, int);
1104 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1105 static void PlayLevelSoundActionIfLoop(int, int, int);
1106 static void StopLevelSoundActionIfLoop(int, int, int);
1107 static void PlayLevelMusic();
1108
1109 static void MapGameButtons();
1110 static void HandleGameButtons(struct GadgetInfo *);
1111
1112 int AmoebeNachbarNr(int, int);
1113 void AmoebeUmwandeln(int, int);
1114 void ContinueMoving(int, int);
1115 void Bang(int, int);
1116 void InitMovDir(int, int);
1117 void InitAmoebaNr(int, int);
1118 int NewHiScore(void);
1119
1120 void TestIfGoodThingHitsBadThing(int, int, int);
1121 void TestIfBadThingHitsGoodThing(int, int, int);
1122 void TestIfPlayerTouchesBadThing(int, int);
1123 void TestIfPlayerRunsIntoBadThing(int, int, int);
1124 void TestIfBadThingTouchesPlayer(int, int);
1125 void TestIfBadThingRunsIntoPlayer(int, int, int);
1126 void TestIfFriendTouchesBadThing(int, int);
1127 void TestIfBadThingTouchesFriend(int, int);
1128 void TestIfBadThingTouchesOtherBadThing(int, int);
1129 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1130
1131 void KillPlayer(struct PlayerInfo *);
1132 void BuryPlayer(struct PlayerInfo *);
1133 void RemovePlayer(struct PlayerInfo *);
1134
1135 static int getInvisibleActiveFromInvisibleElement(int);
1136 static int getInvisibleFromInvisibleActiveElement(int);
1137
1138 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1139
1140 /* for detection of endless loops, caused by custom element programming */
1141 /* (using maximal playfield width x 10 is just a rough approximation) */
1142 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1143
1144 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1145 {                                                                       \
1146   if (recursion_loop_detected)                                          \
1147     return (rc);                                                        \
1148                                                                         \
1149   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1150   {                                                                     \
1151     recursion_loop_detected = TRUE;                                     \
1152     recursion_loop_element = (e);                                       \
1153   }                                                                     \
1154                                                                         \
1155   recursion_loop_depth++;                                               \
1156 }
1157
1158 #define RECURSION_LOOP_DETECTION_END()                                  \
1159 {                                                                       \
1160   recursion_loop_depth--;                                               \
1161 }
1162
1163 static int recursion_loop_depth;
1164 static boolean recursion_loop_detected;
1165 static boolean recursion_loop_element;
1166
1167
1168 /* ------------------------------------------------------------------------- */
1169 /* definition of elements that automatically change to other elements after  */
1170 /* a specified time, eventually calling a function when changing             */
1171 /* ------------------------------------------------------------------------- */
1172
1173 /* forward declaration for changer functions */
1174 static void InitBuggyBase(int, int);
1175 static void WarnBuggyBase(int, int);
1176
1177 static void InitTrap(int, int);
1178 static void ActivateTrap(int, int);
1179 static void ChangeActiveTrap(int, int);
1180
1181 static void InitRobotWheel(int, int);
1182 static void RunRobotWheel(int, int);
1183 static void StopRobotWheel(int, int);
1184
1185 static void InitTimegateWheel(int, int);
1186 static void RunTimegateWheel(int, int);
1187
1188 static void InitMagicBallDelay(int, int);
1189 static void ActivateMagicBall(int, int);
1190
1191 struct ChangingElementInfo
1192 {
1193   int element;
1194   int target_element;
1195   int change_delay;
1196   void (*pre_change_function)(int x, int y);
1197   void (*change_function)(int x, int y);
1198   void (*post_change_function)(int x, int y);
1199 };
1200
1201 static struct ChangingElementInfo change_delay_list[] =
1202 {
1203   {
1204     EL_NUT_BREAKING,
1205     EL_EMERALD,
1206     6,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_PEARL_BREAKING,
1213     EL_EMPTY,
1214     8,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_EXIT_OPENING,
1221     EL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_EXIT_CLOSING,
1229     EL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_STEEL_EXIT_OPENING,
1237     EL_STEEL_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_STEEL_EXIT_CLOSING,
1245     EL_STEEL_EXIT_CLOSED,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_EXIT_OPENING,
1253     EL_EM_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_EXIT_CLOSING,
1261 #if 1
1262     EL_EMPTY,
1263 #else
1264     EL_EM_EXIT_CLOSED,
1265 #endif
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_OPENING,
1273     EL_EM_STEEL_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_STEEL_EXIT_CLOSING,
1281 #if 1
1282     EL_STEELWALL,
1283 #else
1284     EL_EM_STEEL_EXIT_CLOSED,
1285 #endif
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_EXIT_OPENING,
1293     EL_SP_EXIT_OPEN,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SP_EXIT_CLOSING,
1301     EL_SP_EXIT_CLOSED,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_SWITCHGATE_OPENING,
1309     EL_SWITCHGATE_OPEN,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_SWITCHGATE_CLOSING,
1317     EL_SWITCHGATE_CLOSED,
1318     29,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_TIMEGATE_OPENING,
1325     EL_TIMEGATE_OPEN,
1326     29,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_TIMEGATE_CLOSING,
1333     EL_TIMEGATE_CLOSED,
1334     29,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339
1340   {
1341     EL_ACID_SPLASH_LEFT,
1342     EL_EMPTY,
1343     8,
1344     NULL,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_ACID_SPLASH_RIGHT,
1350     EL_EMPTY,
1351     8,
1352     NULL,
1353     NULL,
1354     NULL
1355   },
1356   {
1357     EL_SP_BUGGY_BASE,
1358     EL_SP_BUGGY_BASE_ACTIVATING,
1359     0,
1360     InitBuggyBase,
1361     NULL,
1362     NULL
1363   },
1364   {
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     EL_SP_BUGGY_BASE_ACTIVE,
1367     0,
1368     InitBuggyBase,
1369     NULL,
1370     NULL
1371   },
1372   {
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     EL_SP_BUGGY_BASE,
1375     0,
1376     InitBuggyBase,
1377     WarnBuggyBase,
1378     NULL
1379   },
1380   {
1381     EL_TRAP,
1382     EL_TRAP_ACTIVE,
1383     0,
1384     InitTrap,
1385     NULL,
1386     ActivateTrap
1387   },
1388   {
1389     EL_TRAP_ACTIVE,
1390     EL_TRAP,
1391     31,
1392     NULL,
1393     ChangeActiveTrap,
1394     NULL
1395   },
1396   {
1397     EL_ROBOT_WHEEL_ACTIVE,
1398     EL_ROBOT_WHEEL,
1399     0,
1400     InitRobotWheel,
1401     RunRobotWheel,
1402     StopRobotWheel
1403   },
1404   {
1405     EL_TIMEGATE_SWITCH_ACTIVE,
1406     EL_TIMEGATE_SWITCH,
1407     0,
1408     InitTimegateWheel,
1409     RunTimegateWheel,
1410     NULL
1411   },
1412   {
1413     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1414     EL_DC_TIMEGATE_SWITCH,
1415     0,
1416     InitTimegateWheel,
1417     RunTimegateWheel,
1418     NULL
1419   },
1420   {
1421     EL_EMC_MAGIC_BALL_ACTIVE,
1422     EL_EMC_MAGIC_BALL_ACTIVE,
1423     0,
1424     InitMagicBallDelay,
1425     NULL,
1426     ActivateMagicBall
1427   },
1428   {
1429     EL_EMC_SPRING_BUMPER_ACTIVE,
1430     EL_EMC_SPRING_BUMPER,
1431     8,
1432     NULL,
1433     NULL,
1434     NULL
1435   },
1436   {
1437     EL_DIAGONAL_SHRINKING,
1438     EL_UNDEFINED,
1439     0,
1440     NULL,
1441     NULL,
1442     NULL
1443   },
1444   {
1445     EL_DIAGONAL_GROWING,
1446     EL_UNDEFINED,
1447     0,
1448     NULL,
1449     NULL,
1450     NULL,
1451   },
1452
1453   {
1454     EL_UNDEFINED,
1455     EL_UNDEFINED,
1456     -1,
1457     NULL,
1458     NULL,
1459     NULL
1460   }
1461 };
1462
1463 struct
1464 {
1465   int element;
1466   int push_delay_fixed, push_delay_random;
1467 }
1468 push_delay_list[] =
1469 {
1470   { EL_SPRING,                  0, 0 },
1471   { EL_BALLOON,                 0, 0 },
1472
1473   { EL_SOKOBAN_OBJECT,          2, 0 },
1474   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1475   { EL_SATELLITE,               2, 0 },
1476   { EL_SP_DISK_YELLOW,          2, 0 },
1477
1478   { EL_UNDEFINED,               0, 0 },
1479 };
1480
1481 struct
1482 {
1483   int element;
1484   int move_stepsize;
1485 }
1486 move_stepsize_list[] =
1487 {
1488   { EL_AMOEBA_DROP,             2 },
1489   { EL_AMOEBA_DROPPING,         2 },
1490   { EL_QUICKSAND_FILLING,       1 },
1491   { EL_QUICKSAND_EMPTYING,      1 },
1492   { EL_QUICKSAND_FAST_FILLING,  2 },
1493   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1494   { EL_MAGIC_WALL_FILLING,      2 },
1495   { EL_MAGIC_WALL_EMPTYING,     2 },
1496   { EL_BD_MAGIC_WALL_FILLING,   2 },
1497   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1498   { EL_DC_MAGIC_WALL_FILLING,   2 },
1499   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int count;
1508 }
1509 collect_count_list[] =
1510 {
1511   { EL_EMERALD,                 1 },
1512   { EL_BD_DIAMOND,              1 },
1513   { EL_EMERALD_YELLOW,          1 },
1514   { EL_EMERALD_RED,             1 },
1515   { EL_EMERALD_PURPLE,          1 },
1516   { EL_DIAMOND,                 3 },
1517   { EL_SP_INFOTRON,             1 },
1518   { EL_PEARL,                   5 },
1519   { EL_CRYSTAL,                 8 },
1520
1521   { EL_UNDEFINED,               0 },
1522 };
1523
1524 struct
1525 {
1526   int element;
1527   int direction;
1528 }
1529 access_direction_list[] =
1530 {
1531   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1532   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1533   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1534   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1535   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1536   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1537   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1538   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1539   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1540   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1541   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1542
1543   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1544   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1545   { EL_SP_PORT_UP,                                                   MV_DOWN },
1546   { EL_SP_PORT_DOWN,                                         MV_UP           },
1547   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1548   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1549   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1550   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1551   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1552   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1553   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1554   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1555   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1556   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1557   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1558   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1559   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1560   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1561   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1562
1563   { EL_UNDEFINED,                       MV_NONE                              }
1564 };
1565
1566 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1567
1568 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1569 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1570 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1571                                  IS_JUST_CHANGING(x, y))
1572
1573 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1574
1575 /* static variables for playfield scan mode (scanning forward or backward) */
1576 static int playfield_scan_start_x = 0;
1577 static int playfield_scan_start_y = 0;
1578 static int playfield_scan_delta_x = 1;
1579 static int playfield_scan_delta_y = 1;
1580
1581 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1582                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1583                                      (y) += playfield_scan_delta_y)     \
1584                                 for ((x) = playfield_scan_start_x;      \
1585                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1586                                      (x) += playfield_scan_delta_x)
1587
1588 #ifdef DEBUG
1589 void DEBUG_SetMaximumDynamite()
1590 {
1591   int i;
1592
1593   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1594     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1595       local_player->inventory_element[local_player->inventory_size++] =
1596         EL_DYNAMITE;
1597 }
1598 #endif
1599
1600 static void InitPlayfieldScanModeVars()
1601 {
1602   if (game.use_reverse_scan_direction)
1603   {
1604     playfield_scan_start_x = lev_fieldx - 1;
1605     playfield_scan_start_y = lev_fieldy - 1;
1606
1607     playfield_scan_delta_x = -1;
1608     playfield_scan_delta_y = -1;
1609   }
1610   else
1611   {
1612     playfield_scan_start_x = 0;
1613     playfield_scan_start_y = 0;
1614
1615     playfield_scan_delta_x = 1;
1616     playfield_scan_delta_y = 1;
1617   }
1618 }
1619
1620 static void InitPlayfieldScanMode(int mode)
1621 {
1622   game.use_reverse_scan_direction =
1623     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1624
1625   InitPlayfieldScanModeVars();
1626 }
1627
1628 static int get_move_delay_from_stepsize(int move_stepsize)
1629 {
1630   move_stepsize =
1631     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1632
1633   /* make sure that stepsize value is always a power of 2 */
1634   move_stepsize = (1 << log_2(move_stepsize));
1635
1636   return TILEX / move_stepsize;
1637 }
1638
1639 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1640                                boolean init_game)
1641 {
1642   int player_nr = player->index_nr;
1643   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1644   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1645
1646   /* do no immediately change move delay -- the player might just be moving */
1647   player->move_delay_value_next = move_delay;
1648
1649   /* information if player can move must be set separately */
1650   player->cannot_move = cannot_move;
1651
1652   if (init_game)
1653   {
1654     player->move_delay       = game.initial_move_delay[player_nr];
1655     player->move_delay_value = game.initial_move_delay_value[player_nr];
1656
1657     player->move_delay_value_next = -1;
1658
1659     player->move_delay_reset_counter = 0;
1660   }
1661 }
1662
1663 void GetPlayerConfig()
1664 {
1665   GameFrameDelay = setup.game_frame_delay;
1666
1667   if (!audio.sound_available)
1668     setup.sound_simple = FALSE;
1669
1670   if (!audio.loops_available)
1671     setup.sound_loops = FALSE;
1672
1673   if (!audio.music_available)
1674     setup.sound_music = FALSE;
1675
1676   if (!video.fullscreen_available)
1677     setup.fullscreen = FALSE;
1678
1679   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1680
1681   SetAudioMode(setup.sound);
1682   InitJoysticks();
1683 }
1684
1685 int GetElementFromGroupElement(int element)
1686 {
1687   if (IS_GROUP_ELEMENT(element))
1688   {
1689     struct ElementGroupInfo *group = element_info[element].group;
1690     int last_anim_random_frame = gfx.anim_random_frame;
1691     int element_pos;
1692
1693     if (group->choice_mode == ANIM_RANDOM)
1694       gfx.anim_random_frame = RND(group->num_elements_resolved);
1695
1696     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1697                                     group->choice_mode, 0,
1698                                     group->choice_pos);
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = last_anim_random_frame;
1702
1703     group->choice_pos++;
1704
1705     element = group->element_resolved[element_pos];
1706   }
1707
1708   return element;
1709 }
1710
1711 static void InitPlayerField(int x, int y, int element, boolean init_game)
1712 {
1713   if (element == EL_SP_MURPHY)
1714   {
1715     if (init_game)
1716     {
1717       if (stored_player[0].present)
1718       {
1719         Feld[x][y] = EL_SP_MURPHY_CLONE;
1720
1721         return;
1722       }
1723       else
1724       {
1725         stored_player[0].initial_element = element;
1726         stored_player[0].use_murphy = TRUE;
1727
1728         if (!level.use_artwork_element[0])
1729           stored_player[0].artwork_element = EL_SP_MURPHY;
1730       }
1731
1732       Feld[x][y] = EL_PLAYER_1;
1733     }
1734   }
1735
1736   if (init_game)
1737   {
1738     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1739     int jx = player->jx, jy = player->jy;
1740
1741     player->present = TRUE;
1742
1743     player->block_last_field = (element == EL_SP_MURPHY ?
1744                                 level.sp_block_last_field :
1745                                 level.block_last_field);
1746
1747     /* ---------- initialize player's last field block delay --------------- */
1748
1749     /* always start with reliable default value (no adjustment needed) */
1750     player->block_delay_adjustment = 0;
1751
1752     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1753     if (player->block_last_field && element == EL_SP_MURPHY)
1754       player->block_delay_adjustment = 1;
1755
1756     /* special case 2: in game engines before 3.1.1, blocking was different */
1757     if (game.use_block_last_field_bug)
1758       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1759
1760     if (!options.network || player->connected)
1761     {
1762       player->active = TRUE;
1763
1764       /* remove potentially duplicate players */
1765       if (StorePlayer[jx][jy] == Feld[x][y])
1766         StorePlayer[jx][jy] = 0;
1767
1768       StorePlayer[x][y] = Feld[x][y];
1769
1770       if (options.debug)
1771       {
1772         printf("Player %d activated.\n", player->element_nr);
1773         printf("[Local player is %d and currently %s.]\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778
1779     Feld[x][y] = EL_EMPTY;
1780
1781     player->jx = player->last_jx = x;
1782     player->jy = player->last_jy = y;
1783   }
1784 }
1785
1786 static void InitField(int x, int y, boolean init_game)
1787 {
1788   int element = Feld[x][y];
1789
1790   switch (element)
1791   {
1792     case EL_SP_MURPHY:
1793     case EL_PLAYER_1:
1794     case EL_PLAYER_2:
1795     case EL_PLAYER_3:
1796     case EL_PLAYER_4:
1797       InitPlayerField(x, y, element, init_game);
1798       break;
1799
1800     case EL_SOKOBAN_FIELD_PLAYER:
1801       element = Feld[x][y] = EL_PLAYER_1;
1802       InitField(x, y, init_game);
1803
1804       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1805       InitField(x, y, init_game);
1806       break;
1807
1808     case EL_SOKOBAN_FIELD_EMPTY:
1809       local_player->sokobanfields_still_needed++;
1810       break;
1811
1812     case EL_STONEBLOCK:
1813       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1814         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1815       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1816         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1817       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1818         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1819       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1820         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1821       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1822         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1823       break;
1824
1825     case EL_BUG:
1826     case EL_BUG_RIGHT:
1827     case EL_BUG_UP:
1828     case EL_BUG_LEFT:
1829     case EL_BUG_DOWN:
1830     case EL_SPACESHIP:
1831     case EL_SPACESHIP_RIGHT:
1832     case EL_SPACESHIP_UP:
1833     case EL_SPACESHIP_LEFT:
1834     case EL_SPACESHIP_DOWN:
1835     case EL_BD_BUTTERFLY:
1836     case EL_BD_BUTTERFLY_RIGHT:
1837     case EL_BD_BUTTERFLY_UP:
1838     case EL_BD_BUTTERFLY_LEFT:
1839     case EL_BD_BUTTERFLY_DOWN:
1840     case EL_BD_FIREFLY:
1841     case EL_BD_FIREFLY_RIGHT:
1842     case EL_BD_FIREFLY_UP:
1843     case EL_BD_FIREFLY_LEFT:
1844     case EL_BD_FIREFLY_DOWN:
1845     case EL_PACMAN_RIGHT:
1846     case EL_PACMAN_UP:
1847     case EL_PACMAN_LEFT:
1848     case EL_PACMAN_DOWN:
1849     case EL_YAMYAM:
1850     case EL_YAMYAM_LEFT:
1851     case EL_YAMYAM_RIGHT:
1852     case EL_YAMYAM_UP:
1853     case EL_YAMYAM_DOWN:
1854     case EL_DARK_YAMYAM:
1855     case EL_ROBOT:
1856     case EL_PACMAN:
1857     case EL_SP_SNIKSNAK:
1858     case EL_SP_ELECTRON:
1859     case EL_MOLE:
1860     case EL_MOLE_LEFT:
1861     case EL_MOLE_RIGHT:
1862     case EL_MOLE_UP:
1863     case EL_MOLE_DOWN:
1864       InitMovDir(x, y);
1865       break;
1866
1867     case EL_AMOEBA_FULL:
1868     case EL_BD_AMOEBA:
1869       InitAmoebaNr(x, y);
1870       break;
1871
1872     case EL_AMOEBA_DROP:
1873       if (y == lev_fieldy - 1)
1874       {
1875         Feld[x][y] = EL_AMOEBA_GROWING;
1876         Store[x][y] = EL_AMOEBA_WET;
1877       }
1878       break;
1879
1880     case EL_DYNAMITE_ACTIVE:
1881     case EL_SP_DISK_RED_ACTIVE:
1882     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1883     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1884     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1885     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1886       MovDelay[x][y] = 96;
1887       break;
1888
1889     case EL_EM_DYNAMITE_ACTIVE:
1890       MovDelay[x][y] = 32;
1891       break;
1892
1893     case EL_LAMP:
1894       local_player->lights_still_needed++;
1895       break;
1896
1897     case EL_PENGUIN:
1898       local_player->friends_still_needed++;
1899       break;
1900
1901     case EL_PIG:
1902     case EL_DRAGON:
1903       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1904       break;
1905
1906     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1907     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1908     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1909     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1910     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1911     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1912     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1913     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1914     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1915     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1916     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1917     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1918       if (init_game)
1919       {
1920         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1921         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1922         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1923
1924         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1925         {
1926           game.belt_dir[belt_nr] = belt_dir;
1927           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1928         }
1929         else    /* more than one switch -- set it like the first switch */
1930         {
1931           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1932         }
1933       }
1934       break;
1935
1936 #if !USE_BOTH_SWITCHGATE_SWITCHES
1937     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1938       if (init_game)
1939         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1940       break;
1941
1942     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1943       if (init_game)
1944         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1945       break;
1946 #endif
1947
1948     case EL_LIGHT_SWITCH_ACTIVE:
1949       if (init_game)
1950         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1951       break;
1952
1953     case EL_INVISIBLE_STEELWALL:
1954     case EL_INVISIBLE_WALL:
1955     case EL_INVISIBLE_SAND:
1956       if (game.light_time_left > 0 ||
1957           game.lenses_time_left > 0)
1958         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1959       break;
1960
1961     case EL_EMC_MAGIC_BALL:
1962       if (game.ball_state)
1963         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1964       break;
1965
1966     case EL_EMC_MAGIC_BALL_SWITCH:
1967       if (game.ball_state)
1968         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1969       break;
1970
1971     case EL_TRIGGER_PLAYER:
1972     case EL_TRIGGER_ELEMENT:
1973     case EL_TRIGGER_CE_VALUE:
1974     case EL_TRIGGER_CE_SCORE:
1975     case EL_SELF:
1976     case EL_ANY_ELEMENT:
1977     case EL_CURRENT_CE_VALUE:
1978     case EL_CURRENT_CE_SCORE:
1979     case EL_PREV_CE_1:
1980     case EL_PREV_CE_2:
1981     case EL_PREV_CE_3:
1982     case EL_PREV_CE_4:
1983     case EL_PREV_CE_5:
1984     case EL_PREV_CE_6:
1985     case EL_PREV_CE_7:
1986     case EL_PREV_CE_8:
1987     case EL_NEXT_CE_1:
1988     case EL_NEXT_CE_2:
1989     case EL_NEXT_CE_3:
1990     case EL_NEXT_CE_4:
1991     case EL_NEXT_CE_5:
1992     case EL_NEXT_CE_6:
1993     case EL_NEXT_CE_7:
1994     case EL_NEXT_CE_8:
1995       /* reference elements should not be used on the playfield */
1996       Feld[x][y] = EL_EMPTY;
1997       break;
1998
1999     default:
2000       if (IS_CUSTOM_ELEMENT(element))
2001       {
2002         if (CAN_MOVE(element))
2003           InitMovDir(x, y);
2004
2005 #if USE_NEW_CUSTOM_VALUE
2006         if (!element_info[element].use_last_ce_value || init_game)
2007           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2008 #endif
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   /* not needed to call InitMovDir() -- already done by InitField()! */
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   /* not needed to call InitMovDir() -- already done by InitField()! */
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 #if 1
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   /* pos >= 0: get element from bottom of the stack;
2080      pos <  0: get element from top of the stack */
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 void InitGameControlValues()
2127 {
2128   int i;
2129
2130   for (i = 0; game_panel_controls[i].nr != -1; i++)
2131   {
2132     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2133     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2134     struct TextPosInfo *pos = gpc->pos;
2135     int nr = gpc->nr;
2136     int type = gpc->type;
2137
2138     if (nr != i)
2139     {
2140       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2141       Error(ERR_EXIT, "this should not happen -- please debug");
2142     }
2143
2144     /* force update of game controls after initialization */
2145     gpc->value = gpc->last_value = -1;
2146     gpc->frame = gpc->last_frame = -1;
2147     gpc->gfx_frame = -1;
2148
2149     /* determine panel value width for later calculation of alignment */
2150     if (type == TYPE_INTEGER || type == TYPE_STRING)
2151     {
2152       pos->width = pos->size * getFontWidth(pos->font);
2153       pos->height = getFontHeight(pos->font);
2154     }
2155     else if (type == TYPE_ELEMENT)
2156     {
2157       pos->width = pos->size;
2158       pos->height = pos->size;
2159     }
2160
2161     /* fill structure for game panel draw order */
2162     gpo->nr = gpc->nr;
2163     gpo->sort_priority = pos->sort_priority;
2164   }
2165
2166   /* sort game panel controls according to sort_priority and control number */
2167   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2168         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2169 }
2170
2171 void UpdatePlayfieldElementCount()
2172 {
2173   boolean use_element_count = FALSE;
2174   int i, j, x, y;
2175
2176   /* first check if it is needed at all to calculate playfield element count */
2177   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2178     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2179       use_element_count = TRUE;
2180
2181   if (!use_element_count)
2182     return;
2183
2184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2185     element_info[i].element_count = 0;
2186
2187   SCAN_PLAYFIELD(x, y)
2188   {
2189     element_info[Feld[x][y]].element_count++;
2190   }
2191
2192   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2193     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2194       if (IS_IN_GROUP(j, i))
2195         element_info[EL_GROUP_START + i].element_count +=
2196           element_info[j].element_count;
2197 }
2198
2199 void UpdateGameControlValues()
2200 {
2201   int i, k;
2202   int time = (local_player->LevelSolved ?
2203               local_player->LevelSolved_CountingTime :
2204               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205               level.native_em_level->lev->time :
2206               level.time == 0 ? TimePlayed : TimeLeft);
2207   int score = (local_player->LevelSolved ?
2208                local_player->LevelSolved_CountingScore :
2209                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210                level.native_em_level->lev->score :
2211                local_player->score);
2212   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->required :
2214               local_player->gems_still_needed);
2215   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216                      level.native_em_level->lev->required > 0 :
2217                      local_player->gems_still_needed > 0 ||
2218                      local_player->sokobanfields_still_needed > 0 ||
2219                      local_player->lights_still_needed > 0);
2220
2221   UpdatePlayfieldElementCount();
2222
2223   /* update game panel control values */
2224
2225   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2226   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2227
2228   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2229   for (i = 0; i < MAX_NUM_KEYS; i++)
2230     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2231   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2233
2234   if (game.centered_player_nr == -1)
2235   {
2236     for (i = 0; i < MAX_PLAYERS; i++)
2237     {
2238       for (k = 0; k < MAX_NUM_KEYS; k++)
2239       {
2240         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241         {
2242           if (level.native_em_level->ply[i]->keys & (1 << k))
2243             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2244               get_key_element_from_nr(k);
2245         }
2246         else if (stored_player[i].key[k])
2247           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2248             get_key_element_from_nr(k);
2249       }
2250
2251       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           level.native_em_level->ply[i]->dynamite;
2254       else
2255         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256           stored_player[i].inventory_size;
2257
2258       if (stored_player[i].num_white_keys > 0)
2259         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2260           EL_DC_KEY_WHITE;
2261
2262       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2263         stored_player[i].num_white_keys;
2264     }
2265   }
2266   else
2267   {
2268     int player_nr = game.centered_player_nr;
2269
2270     for (k = 0; k < MAX_NUM_KEYS; k++)
2271     {
2272       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2273       {
2274         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2275           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2276             get_key_element_from_nr(k);
2277       }
2278       else if (stored_player[player_nr].key[k])
2279         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280           get_key_element_from_nr(k);
2281     }
2282
2283     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285         level.native_em_level->ply[player_nr]->dynamite;
2286     else
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         stored_player[player_nr].inventory_size;
2289
2290     if (stored_player[player_nr].num_white_keys > 0)
2291       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292
2293     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294       stored_player[player_nr].num_white_keys;
2295   }
2296
2297   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298   {
2299     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, i);
2301     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302       get_inventory_element_from_pos(local_player, -i - 1);
2303   }
2304
2305   game_panel_controls[GAME_PANEL_SCORE].value = score;
2306   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307
2308   game_panel_controls[GAME_PANEL_TIME].value = time;
2309
2310   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313
2314   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2315     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2316      EL_EMPTY);
2317   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2318     local_player->shield_normal_time_left;
2319   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2320     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2321      EL_EMPTY);
2322   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2323     local_player->shield_deadly_time_left;
2324
2325   game_panel_controls[GAME_PANEL_EXIT].value =
2326     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2327
2328   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2329     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2330   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2331     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2332      EL_EMC_MAGIC_BALL_SWITCH);
2333
2334   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2335     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2336   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2337     game.light_time_left;
2338
2339   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2340     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2341   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2342     game.timegate_time_left;
2343
2344   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2345     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2346
2347   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2348     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2349   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2350     game.lenses_time_left;
2351
2352   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2353     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2354   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2355     game.magnify_time_left;
2356
2357   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2358     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2359      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2360      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2361      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2362      EL_BALLOON_SWITCH_NONE);
2363
2364   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2365     local_player->dynabomb_count;
2366   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2367     local_player->dynabomb_size;
2368   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2369     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2370
2371   game_panel_controls[GAME_PANEL_PENGUINS].value =
2372     local_player->friends_still_needed;
2373
2374   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2375     local_player->sokobanfields_still_needed;
2376   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2377     local_player->sokobanfields_still_needed;
2378
2379   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2380     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2381
2382   for (i = 0; i < NUM_BELTS; i++)
2383   {
2384     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2385       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2386        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2387     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2388       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2389   }
2390
2391   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2392     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2393   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2394     game.magic_wall_time_left;
2395
2396 #if USE_PLAYER_GRAVITY
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398     local_player->gravity;
2399 #else
2400   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2401 #endif
2402
2403   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2404     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2405
2406   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2407     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2408       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2409        game.panel.element[i].id : EL_UNDEFINED);
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2413       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2414        element_info[game.panel.element_count[i].id].element_count : 0);
2415
2416   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2417     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2418       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2419        element_info[game.panel.ce_score[i].id].collect_score : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2424        element_info[game.panel.ce_score_element[i].id].collect_score :
2425        EL_UNDEFINED);
2426
2427   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2428   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2429   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2430
2431   /* update game panel control frames */
2432
2433   for (i = 0; game_panel_controls[i].nr != -1; i++)
2434   {
2435     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2436
2437     if (gpc->type == TYPE_ELEMENT)
2438     {
2439       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2440       {
2441         int last_anim_random_frame = gfx.anim_random_frame;
2442         int element = gpc->value;
2443         int graphic = el2panelimg(element);
2444
2445         if (gpc->value != gpc->last_value)
2446         {
2447           gpc->gfx_frame = 0;
2448           gpc->gfx_random = INIT_GFX_RANDOM();
2449         }
2450         else
2451         {
2452           gpc->gfx_frame++;
2453
2454           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2455               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2456             gpc->gfx_random = INIT_GFX_RANDOM();
2457         }
2458
2459         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2460           gfx.anim_random_frame = gpc->gfx_random;
2461
2462         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2463           gpc->gfx_frame = element_info[element].collect_score;
2464
2465         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2466                                               gpc->gfx_frame);
2467
2468         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2469           gfx.anim_random_frame = last_anim_random_frame;
2470       }
2471     }
2472   }
2473 }
2474
2475 void DisplayGameControlValues()
2476 {
2477   boolean redraw_panel = FALSE;
2478   int i;
2479
2480   for (i = 0; game_panel_controls[i].nr != -1; i++)
2481   {
2482     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2483
2484     if (PANEL_DEACTIVATED(gpc->pos))
2485       continue;
2486
2487     if (gpc->value == gpc->last_value &&
2488         gpc->frame == gpc->last_frame)
2489       continue;
2490
2491     redraw_panel = TRUE;
2492   }
2493
2494   if (!redraw_panel)
2495     return;
2496
2497   /* copy default game door content to main double buffer */
2498   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2499              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2500
2501   /* redraw game control buttons */
2502 #if 1
2503   RedrawGameButtons();
2504 #else
2505   UnmapGameButtons();
2506   MapGameButtons();
2507 #endif
2508
2509   game_status = GAME_MODE_PSEUDO_PANEL;
2510
2511 #if 1
2512   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2513 #else
2514   for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 #endif
2516   {
2517 #if 1
2518     int nr = game_panel_order[i].nr;
2519     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2520 #else
2521     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2522     int nr = gpc->nr;
2523 #endif
2524     struct TextPosInfo *pos = gpc->pos;
2525     int type = gpc->type;
2526     int value = gpc->value;
2527     int frame = gpc->frame;
2528 #if 0
2529     int last_value = gpc->last_value;
2530     int last_frame = gpc->last_frame;
2531 #endif
2532     int size = pos->size;
2533     int font = pos->font;
2534     boolean draw_masked = pos->draw_masked;
2535     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2536
2537     if (PANEL_DEACTIVATED(pos))
2538       continue;
2539
2540 #if 0
2541     if (value == last_value && frame == last_frame)
2542       continue;
2543 #endif
2544
2545     gpc->last_value = value;
2546     gpc->last_frame = frame;
2547
2548 #if 0
2549     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2550 #endif
2551
2552     if (type == TYPE_INTEGER)
2553     {
2554       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2555           nr == GAME_PANEL_TIME)
2556       {
2557         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2558
2559         if (use_dynamic_size)           /* use dynamic number of digits */
2560         {
2561           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2562           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2563           int size2 = size1 + 1;
2564           int font1 = pos->font;
2565           int font2 = pos->font_alt;
2566
2567           size = (value < value_change ? size1 : size2);
2568           font = (value < value_change ? font1 : font2);
2569
2570 #if 0
2571           /* clear background if value just changed its size (dynamic digits) */
2572           if ((last_value < value_change) != (value < value_change))
2573           {
2574             int width1 = size1 * getFontWidth(font1);
2575             int width2 = size2 * getFontWidth(font2);
2576             int max_width = MAX(width1, width2);
2577             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2578
2579             pos->width = max_width;
2580
2581             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2582                                        max_width, max_height);
2583           }
2584 #endif
2585         }
2586       }
2587
2588 #if 1
2589       /* correct text size if "digits" is zero or less */
2590       if (size <= 0)
2591         size = strlen(int2str(value, size));
2592
2593       /* dynamically correct text alignment */
2594       pos->width = size * getFontWidth(font);
2595 #endif
2596
2597       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598                   int2str(value, size), font, mask_mode);
2599     }
2600     else if (type == TYPE_ELEMENT)
2601     {
2602       int element, graphic;
2603       Bitmap *src_bitmap;
2604       int src_x, src_y;
2605       int width, height;
2606       int dst_x = PANEL_XPOS(pos);
2607       int dst_y = PANEL_YPOS(pos);
2608
2609 #if 1
2610       if (value != EL_UNDEFINED && value != EL_EMPTY)
2611       {
2612         element = value;
2613         graphic = el2panelimg(value);
2614
2615         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2616
2617 #if 1
2618         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2619           size = TILESIZE;
2620 #endif
2621
2622         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2623                               &src_x, &src_y);
2624
2625         width  = graphic_info[graphic].width  * size / TILESIZE;
2626         height = graphic_info[graphic].height * size / TILESIZE;
2627
2628         if (draw_masked)
2629         {
2630           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2631                         dst_x - src_x, dst_y - src_y);
2632           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2633                            dst_x, dst_y);
2634         }
2635         else
2636         {
2637           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2638                      dst_x, dst_y);
2639         }
2640       }
2641 #else
2642       if (value == EL_UNDEFINED || value == EL_EMPTY)
2643       {
2644         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2645         graphic = el2panelimg(element);
2646
2647         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2648         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2649         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2650       }
2651       else
2652       {
2653         element = value;
2654         graphic = el2panelimg(value);
2655
2656         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2657       }
2658
2659       width  = graphic_info[graphic].width  * size / TILESIZE;
2660       height = graphic_info[graphic].height * size / TILESIZE;
2661
2662       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2663 #endif
2664     }
2665     else if (type == TYPE_STRING)
2666     {
2667       boolean active = (value != 0);
2668       char *state_normal = "off";
2669       char *state_active = "on";
2670       char *state = (active ? state_active : state_normal);
2671       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2672                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2673                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2674                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2675
2676       if (nr == GAME_PANEL_GRAVITY_STATE)
2677       {
2678         int font1 = pos->font;          /* (used for normal state) */
2679         int font2 = pos->font_alt;      /* (used for active state) */
2680 #if 0
2681         int size1 = strlen(state_normal);
2682         int size2 = strlen(state_active);
2683         int width1 = size1 * getFontWidth(font1);
2684         int width2 = size2 * getFontWidth(font2);
2685         int max_width = MAX(width1, width2);
2686         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2687
2688         pos->width = max_width;
2689
2690         /* clear background for values that may have changed its size */
2691         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2692                                    max_width, max_height);
2693 #endif
2694
2695         font = (active ? font2 : font1);
2696       }
2697
2698       if (s != NULL)
2699       {
2700         char *s_cut;
2701
2702 #if 1
2703         if (size <= 0)
2704         {
2705           /* don't truncate output if "chars" is zero or less */
2706           size = strlen(s);
2707
2708           /* dynamically correct text alignment */
2709           pos->width = size * getFontWidth(font);
2710         }
2711 #endif
2712
2713         s_cut = getStringCopyN(s, size);
2714
2715         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2716                     s_cut, font, mask_mode);
2717
2718         free(s_cut);
2719       }
2720     }
2721
2722     redraw_mask |= REDRAW_DOOR_1;
2723   }
2724
2725   game_status = GAME_MODE_PLAYING;
2726 }
2727
2728 void UpdateAndDisplayGameControlValues()
2729 {
2730   if (tape.warp_forward)
2731     return;
2732
2733   UpdateGameControlValues();
2734   DisplayGameControlValues();
2735 }
2736
2737 void DrawGameValue_Emeralds(int value)
2738 {
2739   struct TextPosInfo *pos = &game.panel.gems;
2740 #if 1
2741   int font_nr = pos->font;
2742 #else
2743   int font_nr = FONT_TEXT_2;
2744 #endif
2745   int font_width = getFontWidth(font_nr);
2746   int chars = pos->size;
2747
2748 #if 1
2749   return;       /* !!! USE NEW STUFF !!! */
2750 #endif
2751
2752   if (PANEL_DEACTIVATED(pos))
2753     return;
2754
2755   pos->width = chars * font_width;
2756
2757   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2758 }
2759
2760 void DrawGameValue_Dynamite(int value)
2761 {
2762   struct TextPosInfo *pos = &game.panel.inventory_count;
2763 #if 1
2764   int font_nr = pos->font;
2765 #else
2766   int font_nr = FONT_TEXT_2;
2767 #endif
2768   int font_width = getFontWidth(font_nr);
2769   int chars = pos->size;
2770
2771 #if 1
2772   return;       /* !!! USE NEW STUFF !!! */
2773 #endif
2774
2775   if (PANEL_DEACTIVATED(pos))
2776     return;
2777
2778   pos->width = chars * font_width;
2779
2780   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2781 }
2782
2783 void DrawGameValue_Score(int value)
2784 {
2785   struct TextPosInfo *pos = &game.panel.score;
2786 #if 1
2787   int font_nr = pos->font;
2788 #else
2789   int font_nr = FONT_TEXT_2;
2790 #endif
2791   int font_width = getFontWidth(font_nr);
2792   int chars = pos->size;
2793
2794 #if 1
2795   return;       /* !!! USE NEW STUFF !!! */
2796 #endif
2797
2798   if (PANEL_DEACTIVATED(pos))
2799     return;
2800
2801   pos->width = chars * font_width;
2802
2803   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2804 }
2805
2806 void DrawGameValue_Time(int value)
2807 {
2808   struct TextPosInfo *pos = &game.panel.time;
2809   static int last_value = -1;
2810   int chars1 = 3;
2811   int chars2 = 4;
2812   int chars = pos->size;
2813 #if 1
2814   int font1_nr = pos->font;
2815   int font2_nr = pos->font_alt;
2816 #else
2817   int font1_nr = FONT_TEXT_2;
2818   int font2_nr = FONT_TEXT_1;
2819 #endif
2820   int font_nr = font1_nr;
2821   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822
2823 #if 1
2824   return;       /* !!! USE NEW STUFF !!! */
2825 #endif
2826
2827   if (PANEL_DEACTIVATED(pos))
2828     return;
2829
2830   if (use_dynamic_chars)                /* use dynamic number of chars */
2831   {
2832     chars   = (value < 1000 ? chars1   : chars2);
2833     font_nr = (value < 1000 ? font1_nr : font2_nr);
2834   }
2835
2836   /* clear background if value just changed its size (dynamic chars only) */
2837   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838   {
2839     int width1 = chars1 * getFontWidth(font1_nr);
2840     int width2 = chars2 * getFontWidth(font2_nr);
2841     int max_width = MAX(width1, width2);
2842     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843
2844     pos->width = max_width;
2845
2846     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847                                max_width, max_height);
2848   }
2849
2850   pos->width = chars * getFontWidth(font_nr);
2851
2852   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2853
2854   last_value = value;
2855 }
2856
2857 void DrawGameValue_Level(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.level_number;
2860   int chars1 = 2;
2861   int chars2 = 3;
2862   int chars = pos->size;
2863 #if 1
2864   int font1_nr = pos->font;
2865   int font2_nr = pos->font_alt;
2866 #else
2867   int font1_nr = FONT_TEXT_2;
2868   int font2_nr = FONT_TEXT_1;
2869 #endif
2870   int font_nr = font1_nr;
2871   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2872
2873 #if 1
2874   return;       /* !!! USE NEW STUFF !!! */
2875 #endif
2876
2877   if (PANEL_DEACTIVATED(pos))
2878     return;
2879
2880   if (use_dynamic_chars)                /* use dynamic number of chars */
2881   {
2882     chars   = (level_nr < 100 ? chars1   : chars2);
2883     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2884   }
2885
2886   pos->width = chars * getFontWidth(font_nr);
2887
2888   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2889 }
2890
2891 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2892 {
2893 #if 0
2894   struct TextPosInfo *pos = &game.panel.keys;
2895 #endif
2896 #if 0
2897   int base_key_graphic = EL_KEY_1;
2898 #endif
2899   int i;
2900
2901 #if 1
2902   return;       /* !!! USE NEW STUFF !!! */
2903 #endif
2904
2905 #if 0
2906   if (PANEL_DEACTIVATED(pos))
2907     return;
2908 #endif
2909
2910 #if 0
2911   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2912     base_key_graphic = EL_EM_KEY_1;
2913 #endif
2914
2915 #if 0
2916   pos->width = 4 * MINI_TILEX;
2917 #endif
2918
2919 #if 1
2920   for (i = 0; i < MAX_NUM_KEYS; i++)
2921 #else
2922   /* currently only 4 of 8 possible keys are displayed */
2923   for (i = 0; i < STD_NUM_KEYS; i++)
2924 #endif
2925   {
2926 #if 1
2927     struct TextPosInfo *pos = &game.panel.key[i];
2928 #endif
2929     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2930     int src_y = DOOR_GFX_PAGEY1 + 123;
2931 #if 1
2932     int dst_x = PANEL_XPOS(pos);
2933     int dst_y = PANEL_YPOS(pos);
2934 #else
2935     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2936     int dst_y = PANEL_YPOS(pos);
2937 #endif
2938
2939 #if 1
2940     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2941                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2942                    EL_KEY_1) + i;
2943     int graphic = el2edimg(element);
2944 #endif
2945
2946 #if 1
2947     if (PANEL_DEACTIVATED(pos))
2948       continue;
2949 #endif
2950
2951 #if 0
2952     /* masked blit with tiles from half-size scaled bitmap does not work yet
2953        (no mask bitmap created for these sizes after loading and scaling) --
2954        solution: load without creating mask, scale, then create final mask */
2955
2956     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2957                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2958
2959     if (key[i])
2960     {
2961 #if 0
2962       int graphic = el2edimg(base_key_graphic + i);
2963 #endif
2964       Bitmap *src_bitmap;
2965       int src_x, src_y;
2966
2967       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2968
2969       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2970                     dst_x - src_x, dst_y - src_y);
2971       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2972                        dst_x, dst_y);
2973     }
2974 #else
2975 #if 1
2976     if (key[i])
2977       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2978     else
2979       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2980                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2981 #else
2982     if (key[i])
2983       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2984     else
2985       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2986                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2987 #endif
2988 #endif
2989   }
2990 }
2991
2992 #else
2993
2994 void DrawGameValue_Emeralds(int value)
2995 {
2996   int font_nr = FONT_TEXT_2;
2997   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2998
2999   if (PANEL_DEACTIVATED(game.panel.gems))
3000     return;
3001
3002   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3003 }
3004
3005 void DrawGameValue_Dynamite(int value)
3006 {
3007   int font_nr = FONT_TEXT_2;
3008   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3009
3010   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3011     return;
3012
3013   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3014 }
3015
3016 void DrawGameValue_Score(int value)
3017 {
3018   int font_nr = FONT_TEXT_2;
3019   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3020
3021   if (PANEL_DEACTIVATED(game.panel.score))
3022     return;
3023
3024   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3025 }
3026
3027 void DrawGameValue_Time(int value)
3028 {
3029   int font1_nr = FONT_TEXT_2;
3030 #if 1
3031   int font2_nr = FONT_TEXT_1;
3032 #else
3033   int font2_nr = FONT_LEVEL_NUMBER;
3034 #endif
3035   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3036   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3037
3038   if (PANEL_DEACTIVATED(game.panel.time))
3039     return;
3040
3041   /* clear background if value just changed its size */
3042   if (value == 999 || value == 1000)
3043     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3044
3045   if (value < 1000)
3046     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3047   else
3048     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3049 }
3050
3051 void DrawGameValue_Level(int value)
3052 {
3053   int font1_nr = FONT_TEXT_2;
3054 #if 1
3055   int font2_nr = FONT_TEXT_1;
3056 #else
3057   int font2_nr = FONT_LEVEL_NUMBER;
3058 #endif
3059
3060   if (PANEL_DEACTIVATED(game.panel.level))
3061     return;
3062
3063   if (level_nr < 100)
3064     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3065   else
3066     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3067 }
3068
3069 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3070 {
3071   int base_key_graphic = EL_KEY_1;
3072   int i;
3073
3074   if (PANEL_DEACTIVATED(game.panel.keys))
3075     return;
3076
3077   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3078     base_key_graphic = EL_EM_KEY_1;
3079
3080   /* currently only 4 of 8 possible keys are displayed */
3081   for (i = 0; i < STD_NUM_KEYS; i++)
3082   {
3083     int x = XX_KEYS + i * MINI_TILEX;
3084     int y = YY_KEYS;
3085
3086     if (key[i])
3087       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3088     else
3089       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3090                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3091   }
3092 }
3093
3094 #endif
3095
3096 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3097                        int key_bits)
3098 {
3099   int key[MAX_NUM_KEYS];
3100   int i;
3101
3102   /* prevent EM engine from updating time/score values parallel to GameWon() */
3103   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3104       local_player->LevelSolved)
3105     return;
3106
3107   for (i = 0; i < MAX_NUM_KEYS; i++)
3108     key[i] = key_bits & (1 << i);
3109
3110   DrawGameValue_Level(level_nr);
3111
3112   DrawGameValue_Emeralds(emeralds);
3113   DrawGameValue_Dynamite(dynamite);
3114   DrawGameValue_Score(score);
3115   DrawGameValue_Time(time);
3116
3117   DrawGameValue_Keys(key);
3118 }
3119
3120 void UpdateGameDoorValues()
3121 {
3122   UpdateGameControlValues();
3123 }
3124
3125 void DrawGameDoorValues()
3126 {
3127   DisplayGameControlValues();
3128 }
3129
3130 void DrawGameDoorValues_OLD()
3131 {
3132   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3133   int dynamite_value = 0;
3134   int score_value = (local_player->LevelSolved ? local_player->score_final :
3135                      local_player->score);
3136   int gems_value = local_player->gems_still_needed;
3137   int key_bits = 0;
3138   int i, j;
3139
3140   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3141   {
3142     DrawGameDoorValues_EM();
3143
3144     return;
3145   }
3146
3147   if (game.centered_player_nr == -1)
3148   {
3149     for (i = 0; i < MAX_PLAYERS; i++)
3150     {
3151       for (j = 0; j < MAX_NUM_KEYS; j++)
3152         if (stored_player[i].key[j])
3153           key_bits |= (1 << j);
3154
3155       dynamite_value += stored_player[i].inventory_size;
3156     }
3157   }
3158   else
3159   {
3160     int player_nr = game.centered_player_nr;
3161
3162     for (i = 0; i < MAX_NUM_KEYS; i++)
3163       if (stored_player[player_nr].key[i])
3164         key_bits |= (1 << i);
3165
3166     dynamite_value = stored_player[player_nr].inventory_size;
3167   }
3168
3169   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3170                     key_bits);
3171 }
3172
3173
3174 /*
3175   =============================================================================
3176   InitGameEngine()
3177   -----------------------------------------------------------------------------
3178   initialize game engine due to level / tape version number
3179   =============================================================================
3180 */
3181
3182 static void InitGameEngine()
3183 {
3184   int i, j, k, l, x, y;
3185
3186   /* set game engine from tape file when re-playing, else from level file */
3187   game.engine_version = (tape.playing ? tape.engine_version :
3188                          level.game_version);
3189
3190   /* ---------------------------------------------------------------------- */
3191   /* set flags for bugs and changes according to active game engine version */
3192   /* ---------------------------------------------------------------------- */
3193
3194   /*
3195     Summary of bugfix/change:
3196     Fixed handling for custom elements that change when pushed by the player.
3197
3198     Fixed/changed in version:
3199     3.1.0
3200
3201     Description:
3202     Before 3.1.0, custom elements that "change when pushing" changed directly
3203     after the player started pushing them (until then handled in "DigField()").
3204     Since 3.1.0, these custom elements are not changed until the "pushing"
3205     move of the element is finished (now handled in "ContinueMoving()").
3206
3207     Affected levels/tapes:
3208     The first condition is generally needed for all levels/tapes before version
3209     3.1.0, which might use the old behaviour before it was changed; known tapes
3210     that are affected are some tapes from the level set "Walpurgis Gardens" by
3211     Jamie Cullen.
3212     The second condition is an exception from the above case and is needed for
3213     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3214     above (including some development versions of 3.1.0), but before it was
3215     known that this change would break tapes like the above and was fixed in
3216     3.1.1, so that the changed behaviour was active although the engine version
3217     while recording maybe was before 3.1.0. There is at least one tape that is
3218     affected by this exception, which is the tape for the one-level set "Bug
3219     Machine" by Juergen Bonhagen.
3220   */
3221
3222   game.use_change_when_pushing_bug =
3223     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3224      !(tape.playing &&
3225        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3226        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3227
3228   /*
3229     Summary of bugfix/change:
3230     Fixed handling for blocking the field the player leaves when moving.
3231
3232     Fixed/changed in version:
3233     3.1.1
3234
3235     Description:
3236     Before 3.1.1, when "block last field when moving" was enabled, the field
3237     the player is leaving when moving was blocked for the time of the move,
3238     and was directly unblocked afterwards. This resulted in the last field
3239     being blocked for exactly one less than the number of frames of one player
3240     move. Additionally, even when blocking was disabled, the last field was
3241     blocked for exactly one frame.
3242     Since 3.1.1, due to changes in player movement handling, the last field
3243     is not blocked at all when blocking is disabled. When blocking is enabled,
3244     the last field is blocked for exactly the number of frames of one player
3245     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3246     last field is blocked for exactly one more than the number of frames of
3247     one player move.
3248
3249     Affected levels/tapes:
3250     (!!! yet to be determined -- probably many !!!)
3251   */
3252
3253   game.use_block_last_field_bug =
3254     (game.engine_version < VERSION_IDENT(3,1,1,0));
3255
3256   /*
3257     Summary of bugfix/change:
3258     Changed behaviour of CE changes with multiple changes per single frame.
3259
3260     Fixed/changed in version:
3261     3.2.0-6
3262
3263     Description:
3264     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3265     This resulted in race conditions where CEs seem to behave strange in some
3266     situations (where triggered CE changes were just skipped because there was
3267     already a CE change on that tile in the playfield in that engine frame).
3268     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3269     (The number of changes per frame must be limited in any case, because else
3270     it is easily possible to define CE changes that would result in an infinite
3271     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3272     should be set large enough so that it would only be reached in cases where
3273     the corresponding CE change conditions run into a loop. Therefore, it seems
3274     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3275     maximal number of change pages for custom elements.)
3276
3277     Affected levels/tapes:
3278     Probably many.
3279   */
3280
3281 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3282   game.max_num_changes_per_frame = 1;
3283 #else
3284   game.max_num_changes_per_frame =
3285     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3286 #endif
3287
3288   /* ---------------------------------------------------------------------- */
3289
3290   /* default scan direction: scan playfield from top/left to bottom/right */
3291   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3292
3293   /* dynamically adjust element properties according to game engine version */
3294   InitElementPropertiesEngine(game.engine_version);
3295
3296 #if 0
3297   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3298   printf("          tape version == %06d [%s] [file: %06d]\n",
3299          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3300          tape.file_version);
3301   printf("       => game.engine_version == %06d\n", game.engine_version);
3302 #endif
3303
3304   /* ---------- initialize player's initial move delay --------------------- */
3305
3306   /* dynamically adjust player properties according to level information */
3307   for (i = 0; i < MAX_PLAYERS; i++)
3308     game.initial_move_delay_value[i] =
3309       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3310
3311   /* dynamically adjust player properties according to game engine version */
3312   for (i = 0; i < MAX_PLAYERS; i++)
3313     game.initial_move_delay[i] =
3314       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3315        game.initial_move_delay_value[i] : 0);
3316
3317   /* ---------- initialize player's initial push delay --------------------- */
3318
3319   /* dynamically adjust player properties according to game engine version */
3320   game.initial_push_delay_value =
3321     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3322
3323   /* ---------- initialize changing elements ------------------------------- */
3324
3325   /* initialize changing elements information */
3326   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327   {
3328     struct ElementInfo *ei = &element_info[i];
3329
3330     /* this pointer might have been changed in the level editor */
3331     ei->change = &ei->change_page[0];
3332
3333     if (!IS_CUSTOM_ELEMENT(i))
3334     {
3335       ei->change->target_element = EL_EMPTY_SPACE;
3336       ei->change->delay_fixed = 0;
3337       ei->change->delay_random = 0;
3338       ei->change->delay_frames = 1;
3339     }
3340
3341     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3342     {
3343       ei->has_change_event[j] = FALSE;
3344
3345       ei->event_page_nr[j] = 0;
3346       ei->event_page[j] = &ei->change_page[0];
3347     }
3348   }
3349
3350   /* add changing elements from pre-defined list */
3351   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3352   {
3353     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3354     struct ElementInfo *ei = &element_info[ch_delay->element];
3355
3356     ei->change->target_element       = ch_delay->target_element;
3357     ei->change->delay_fixed          = ch_delay->change_delay;
3358
3359     ei->change->pre_change_function  = ch_delay->pre_change_function;
3360     ei->change->change_function      = ch_delay->change_function;
3361     ei->change->post_change_function = ch_delay->post_change_function;
3362
3363     ei->change->can_change = TRUE;
3364     ei->change->can_change_or_has_action = TRUE;
3365
3366     ei->has_change_event[CE_DELAY] = TRUE;
3367
3368     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3369     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3370   }
3371
3372   /* ---------- initialize internal run-time variables --------------------- */
3373
3374   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3375   {
3376     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3377
3378     for (j = 0; j < ei->num_change_pages; j++)
3379     {
3380       ei->change_page[j].can_change_or_has_action =
3381         (ei->change_page[j].can_change |
3382          ei->change_page[j].has_action);
3383     }
3384   }
3385
3386   /* add change events from custom element configuration */
3387   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3388   {
3389     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3390
3391     for (j = 0; j < ei->num_change_pages; j++)
3392     {
3393       if (!ei->change_page[j].can_change_or_has_action)
3394         continue;
3395
3396       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3397       {
3398         /* only add event page for the first page found with this event */
3399         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3400         {
3401           ei->has_change_event[k] = TRUE;
3402
3403           ei->event_page_nr[k] = j;
3404           ei->event_page[k] = &ei->change_page[j];
3405         }
3406       }
3407     }
3408   }
3409
3410 #if 1
3411   /* ---------- initialize reference elements in change conditions --------- */
3412
3413   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3414   {
3415     int element = EL_CUSTOM_START + i;
3416     struct ElementInfo *ei = &element_info[element];
3417
3418     for (j = 0; j < ei->num_change_pages; j++)
3419     {
3420       int trigger_element = ei->change_page[j].initial_trigger_element;
3421
3422       if (trigger_element >= EL_PREV_CE_8 &&
3423           trigger_element <= EL_NEXT_CE_8)
3424         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3425
3426       ei->change_page[j].trigger_element = trigger_element;
3427     }
3428   }
3429 #endif
3430
3431   /* ---------- initialize run-time trigger player and element ------------- */
3432
3433   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3434   {
3435     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3436
3437     for (j = 0; j < ei->num_change_pages; j++)
3438     {
3439       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3440       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3441       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3442       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3443       ei->change_page[j].actual_trigger_ce_value = 0;
3444       ei->change_page[j].actual_trigger_ce_score = 0;
3445     }
3446   }
3447
3448   /* ---------- initialize trigger events ---------------------------------- */
3449
3450   /* initialize trigger events information */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3453       trigger_events[i][j] = FALSE;
3454
3455   /* add trigger events from element change event properties */
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457   {
3458     struct ElementInfo *ei = &element_info[i];
3459
3460     for (j = 0; j < ei->num_change_pages; j++)
3461     {
3462       if (!ei->change_page[j].can_change_or_has_action)
3463         continue;
3464
3465       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3466       {
3467         int trigger_element = ei->change_page[j].trigger_element;
3468
3469         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3470         {
3471           if (ei->change_page[j].has_event[k])
3472           {
3473             if (IS_GROUP_ELEMENT(trigger_element))
3474             {
3475               struct ElementGroupInfo *group =
3476                 element_info[trigger_element].group;
3477
3478               for (l = 0; l < group->num_elements_resolved; l++)
3479                 trigger_events[group->element_resolved[l]][k] = TRUE;
3480             }
3481             else if (trigger_element == EL_ANY_ELEMENT)
3482               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3483                 trigger_events[l][k] = TRUE;
3484             else
3485               trigger_events[trigger_element][k] = TRUE;
3486           }
3487         }
3488       }
3489     }
3490   }
3491
3492   /* ---------- initialize push delay -------------------------------------- */
3493
3494   /* initialize push delay values to default */
3495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496   {
3497     if (!IS_CUSTOM_ELEMENT(i))
3498     {
3499       /* set default push delay values (corrected since version 3.0.7-1) */
3500       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3501       {
3502         element_info[i].push_delay_fixed = 2;
3503         element_info[i].push_delay_random = 8;
3504       }
3505       else
3506       {
3507         element_info[i].push_delay_fixed = 8;
3508         element_info[i].push_delay_random = 8;
3509       }
3510     }
3511   }
3512
3513   /* set push delay value for certain elements from pre-defined list */
3514   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3515   {
3516     int e = push_delay_list[i].element;
3517
3518     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3519     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3520   }
3521
3522   /* set push delay value for Supaplex elements for newer engine versions */
3523   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3524   {
3525     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3526     {
3527       if (IS_SP_ELEMENT(i))
3528       {
3529         /* set SP push delay to just enough to push under a falling zonk */
3530         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3531
3532         element_info[i].push_delay_fixed  = delay;
3533         element_info[i].push_delay_random = 0;
3534       }
3535     }
3536   }
3537
3538   /* ---------- initialize move stepsize ----------------------------------- */
3539
3540   /* initialize move stepsize values to default */
3541   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3542     if (!IS_CUSTOM_ELEMENT(i))
3543       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3544
3545   /* set move stepsize value for certain elements from pre-defined list */
3546   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3547   {
3548     int e = move_stepsize_list[i].element;
3549
3550     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3551   }
3552
3553   /* ---------- initialize collect score ----------------------------------- */
3554
3555   /* initialize collect score values for custom elements from initial value */
3556   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3557     if (IS_CUSTOM_ELEMENT(i))
3558       element_info[i].collect_score = element_info[i].collect_score_initial;
3559
3560   /* ---------- initialize collect count ----------------------------------- */
3561
3562   /* initialize collect count values for non-custom elements */
3563   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3564     if (!IS_CUSTOM_ELEMENT(i))
3565       element_info[i].collect_count_initial = 0;
3566
3567   /* add collect count values for all elements from pre-defined list */
3568   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3569     element_info[collect_count_list[i].element].collect_count_initial =
3570       collect_count_list[i].count;
3571
3572   /* ---------- initialize access direction -------------------------------- */
3573
3574   /* initialize access direction values to default (access from every side) */
3575   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3576     if (!IS_CUSTOM_ELEMENT(i))
3577       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3578
3579   /* set access direction value for certain elements from pre-defined list */
3580   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3581     element_info[access_direction_list[i].element].access_direction =
3582       access_direction_list[i].direction;
3583
3584   /* ---------- initialize explosion content ------------------------------- */
3585   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586   {
3587     if (IS_CUSTOM_ELEMENT(i))
3588       continue;
3589
3590     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3591     {
3592       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3593
3594       element_info[i].content.e[x][y] =
3595         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3596          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3597          i == EL_PLAYER_3 ? EL_EMERALD :
3598          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3599          i == EL_MOLE ? EL_EMERALD_RED :
3600          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3601          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3602          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3603          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3604          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3605          i == EL_WALL_EMERALD ? EL_EMERALD :
3606          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3607          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3608          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3609          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3610          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3611          i == EL_WALL_PEARL ? EL_PEARL :
3612          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3613          EL_EMPTY);
3614     }
3615   }
3616
3617   /* ---------- initialize recursion detection ------------------------------ */
3618   recursion_loop_depth = 0;
3619   recursion_loop_detected = FALSE;
3620   recursion_loop_element = EL_UNDEFINED;
3621
3622   /* ---------- initialize graphics engine ---------------------------------- */
3623   game.scroll_delay_value =
3624     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3625      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3626   game.scroll_delay_value =
3627     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3628 }
3629
3630 int get_num_special_action(int element, int action_first, int action_last)
3631 {
3632   int num_special_action = 0;
3633   int i, j;
3634
3635   for (i = action_first; i <= action_last; i++)
3636   {
3637     boolean found = FALSE;
3638
3639     for (j = 0; j < NUM_DIRECTIONS; j++)
3640       if (el_act_dir2img(element, i, j) !=
3641           el_act_dir2img(element, ACTION_DEFAULT, j))
3642         found = TRUE;
3643
3644     if (found)
3645       num_special_action++;
3646     else
3647       break;
3648   }
3649
3650   return num_special_action;
3651 }
3652
3653
3654 /*
3655   =============================================================================
3656   InitGame()
3657   -----------------------------------------------------------------------------
3658   initialize and start new game
3659   =============================================================================
3660 */
3661
3662 void InitGame()
3663 {
3664   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3665   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3666   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3667 #if 0
3668   boolean do_fading = (game_status == GAME_MODE_MAIN);
3669 #endif
3670   int i, j, x, y;
3671
3672   game_status = GAME_MODE_PLAYING;
3673
3674   InitGameEngine();
3675   InitGameControlValues();
3676
3677   /* don't play tapes over network */
3678   network_playing = (options.network && !tape.playing);
3679
3680   for (i = 0; i < MAX_PLAYERS; i++)
3681   {
3682     struct PlayerInfo *player = &stored_player[i];
3683
3684     player->index_nr = i;
3685     player->index_bit = (1 << i);
3686     player->element_nr = EL_PLAYER_1 + i;
3687
3688     player->present = FALSE;
3689     player->active = FALSE;
3690     player->killed = FALSE;
3691
3692     player->action = 0;
3693     player->effective_action = 0;
3694     player->programmed_action = 0;
3695
3696     player->score = 0;
3697     player->score_final = 0;
3698
3699     player->gems_still_needed = level.gems_needed;
3700     player->sokobanfields_still_needed = 0;
3701     player->lights_still_needed = 0;
3702     player->friends_still_needed = 0;
3703
3704     for (j = 0; j < MAX_NUM_KEYS; j++)
3705       player->key[j] = FALSE;
3706
3707     player->num_white_keys = 0;
3708
3709     player->dynabomb_count = 0;
3710     player->dynabomb_size = 1;
3711     player->dynabombs_left = 0;
3712     player->dynabomb_xl = FALSE;
3713
3714     player->MovDir = MV_NONE;
3715     player->MovPos = 0;
3716     player->GfxPos = 0;
3717     player->GfxDir = MV_NONE;
3718     player->GfxAction = ACTION_DEFAULT;
3719     player->Frame = 0;
3720     player->StepFrame = 0;
3721
3722     player->initial_element = player->element_nr;
3723     player->artwork_element =
3724       (level.use_artwork_element[i] ? level.artwork_element[i] :
3725        player->element_nr);
3726     player->use_murphy = FALSE;
3727
3728     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3729     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3730
3731     player->gravity = level.initial_player_gravity[i];
3732
3733     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3734
3735     player->actual_frame_counter = 0;
3736
3737     player->step_counter = 0;
3738
3739     player->last_move_dir = MV_NONE;
3740
3741     player->is_active = FALSE;
3742
3743     player->is_waiting = FALSE;
3744     player->is_moving = FALSE;
3745     player->is_auto_moving = FALSE;
3746     player->is_digging = FALSE;
3747     player->is_snapping = FALSE;
3748     player->is_collecting = FALSE;
3749     player->is_pushing = FALSE;
3750     player->is_switching = FALSE;
3751     player->is_dropping = FALSE;
3752     player->is_dropping_pressed = FALSE;
3753
3754     player->is_bored = FALSE;
3755     player->is_sleeping = FALSE;
3756
3757     player->frame_counter_bored = -1;
3758     player->frame_counter_sleeping = -1;
3759
3760     player->anim_delay_counter = 0;
3761     player->post_delay_counter = 0;
3762
3763     player->dir_waiting = MV_NONE;
3764     player->action_waiting = ACTION_DEFAULT;
3765     player->last_action_waiting = ACTION_DEFAULT;
3766     player->special_action_bored = ACTION_DEFAULT;
3767     player->special_action_sleeping = ACTION_DEFAULT;
3768
3769     player->switch_x = -1;
3770     player->switch_y = -1;
3771
3772     player->drop_x = -1;
3773     player->drop_y = -1;
3774
3775     player->show_envelope = 0;
3776
3777     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3778
3779     player->push_delay       = -1;      /* initialized when pushing starts */
3780     player->push_delay_value = game.initial_push_delay_value;
3781
3782     player->drop_delay = 0;
3783     player->drop_pressed_delay = 0;
3784
3785     player->last_jx = -1;
3786     player->last_jy = -1;
3787     player->jx = -1;
3788     player->jy = -1;
3789
3790     player->shield_normal_time_left = 0;
3791     player->shield_deadly_time_left = 0;
3792
3793     player->inventory_infinite_element = EL_UNDEFINED;
3794     player->inventory_size = 0;
3795
3796     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3797     SnapField(player, 0, 0);
3798
3799     player->LevelSolved = FALSE;
3800     player->GameOver = FALSE;
3801
3802     player->LevelSolved_GameWon = FALSE;
3803     player->LevelSolved_GameEnd = FALSE;
3804     player->LevelSolved_PanelOff = FALSE;
3805     player->LevelSolved_SaveTape = FALSE;
3806     player->LevelSolved_SaveScore = FALSE;
3807     player->LevelSolved_CountingTime = 0;
3808     player->LevelSolved_CountingScore = 0;
3809   }
3810
3811   network_player_action_received = FALSE;
3812
3813 #if defined(NETWORK_AVALIABLE)
3814   /* initial null action */
3815   if (network_playing)
3816     SendToServer_MovePlayer(MV_NONE);
3817 #endif
3818
3819   ZX = ZY = -1;
3820   ExitX = ExitY = -1;
3821
3822   FrameCounter = 0;
3823   TimeFrames = 0;
3824   TimePlayed = 0;
3825   TimeLeft = level.time;
3826   TapeTime = 0;
3827
3828   ScreenMovDir = MV_NONE;
3829   ScreenMovPos = 0;
3830   ScreenGfxPos = 0;
3831
3832   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3833
3834   AllPlayersGone = FALSE;
3835
3836   game.yamyam_content_nr = 0;
3837   game.robot_wheel_active = FALSE;
3838   game.magic_wall_active = FALSE;
3839   game.magic_wall_time_left = 0;
3840   game.light_time_left = 0;
3841   game.timegate_time_left = 0;
3842   game.switchgate_pos = 0;
3843   game.wind_direction = level.wind_direction_initial;
3844
3845 #if !USE_PLAYER_GRAVITY
3846   game.gravity = FALSE;
3847   game.explosions_delayed = TRUE;
3848 #endif
3849
3850   game.lenses_time_left = 0;
3851   game.magnify_time_left = 0;
3852
3853   game.ball_state = level.ball_state_initial;
3854   game.ball_content_nr = 0;
3855
3856   game.envelope_active = FALSE;
3857
3858   /* set focus to local player for network games, else to all players */
3859   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3860   game.centered_player_nr_next = game.centered_player_nr;
3861   game.set_centered_player = FALSE;
3862
3863   if (network_playing && tape.recording)
3864   {
3865     /* store client dependent player focus when recording network games */
3866     tape.centered_player_nr_next = game.centered_player_nr_next;
3867     tape.set_centered_player = TRUE;
3868   }
3869
3870   for (i = 0; i < NUM_BELTS; i++)
3871   {
3872     game.belt_dir[i] = MV_NONE;
3873     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3874   }
3875
3876   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3877     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3878
3879   SCAN_PLAYFIELD(x, y)
3880   {
3881     Feld[x][y] = level.field[x][y];
3882     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3883     ChangeDelay[x][y] = 0;
3884     ChangePage[x][y] = -1;
3885 #if USE_NEW_CUSTOM_VALUE
3886     CustomValue[x][y] = 0;              /* initialized in InitField() */
3887 #endif
3888     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3889     AmoebaNr[x][y] = 0;
3890     WasJustMoving[x][y] = 0;
3891     WasJustFalling[x][y] = 0;
3892     CheckCollision[x][y] = 0;
3893     CheckImpact[x][y] = 0;
3894     Stop[x][y] = FALSE;
3895     Pushed[x][y] = FALSE;
3896
3897     ChangeCount[x][y] = 0;
3898     ChangeEvent[x][y] = -1;
3899
3900     ExplodePhase[x][y] = 0;
3901     ExplodeDelay[x][y] = 0;
3902     ExplodeField[x][y] = EX_TYPE_NONE;
3903
3904     RunnerVisit[x][y] = 0;
3905     PlayerVisit[x][y] = 0;
3906
3907     GfxFrame[x][y] = 0;
3908     GfxRandom[x][y] = INIT_GFX_RANDOM();
3909     GfxElement[x][y] = EL_UNDEFINED;
3910     GfxAction[x][y] = ACTION_DEFAULT;
3911     GfxDir[x][y] = MV_NONE;
3912     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3913   }
3914
3915   SCAN_PLAYFIELD(x, y)
3916   {
3917     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3918       emulate_bd = FALSE;
3919     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3920       emulate_sb = FALSE;
3921     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3922       emulate_sp = FALSE;
3923
3924     InitField(x, y, TRUE);
3925
3926     ResetGfxAnimation(x, y);
3927   }
3928
3929   InitBeltMovement();
3930
3931   for (i = 0; i < MAX_PLAYERS; i++)
3932   {
3933     struct PlayerInfo *player = &stored_player[i];
3934
3935     /* set number of special actions for bored and sleeping animation */
3936     player->num_special_action_bored =
3937       get_num_special_action(player->artwork_element,
3938                              ACTION_BORING_1, ACTION_BORING_LAST);
3939     player->num_special_action_sleeping =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3942   }
3943
3944   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3945                     emulate_sb ? EMU_SOKOBAN :
3946                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3947
3948 #if USE_NEW_ALL_SLIPPERY
3949   /* initialize type of slippery elements */
3950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3951   {
3952     if (!IS_CUSTOM_ELEMENT(i))
3953     {
3954       /* default: elements slip down either to the left or right randomly */
3955       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3956
3957       /* SP style elements prefer to slip down on the left side */
3958       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3959         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3960
3961       /* BD style elements prefer to slip down on the left side */
3962       if (game.emulation == EMU_BOULDERDASH)
3963         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3964     }
3965   }
3966 #endif
3967
3968   /* initialize explosion and ignition delay */
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986
3987 #if 0
3988     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3989       element_info[i].explosion_delay = 1;
3990
3991     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3992       element_info[i].ignition_delay = 1;
3993 #endif
3994   }
3995
3996   /* correct non-moving belts to start moving left */
3997   for (i = 0; i < NUM_BELTS; i++)
3998     if (game.belt_dir[i] == MV_NONE)
3999       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4000
4001   /* check if any connected player was not found in playfield */
4002   for (i = 0; i < MAX_PLAYERS; i++)
4003   {
4004     struct PlayerInfo *player = &stored_player[i];
4005
4006     if (player->connected && !player->present)
4007     {
4008       for (j = 0; j < MAX_PLAYERS; j++)
4009       {
4010         struct PlayerInfo *some_player = &stored_player[j];
4011         int jx = some_player->jx, jy = some_player->jy;
4012
4013         /* assign first free player found that is present in the playfield */
4014         if (some_player->present && !some_player->connected)
4015         {
4016           player->present = TRUE;
4017           player->active = TRUE;
4018
4019           some_player->present = FALSE;
4020           some_player->active = FALSE;
4021
4022           player->initial_element = some_player->initial_element;
4023           player->artwork_element = some_player->artwork_element;
4024
4025           player->block_last_field       = some_player->block_last_field;
4026           player->block_delay_adjustment = some_player->block_delay_adjustment;
4027
4028           StorePlayer[jx][jy] = player->element_nr;
4029           player->jx = player->last_jx = jx;
4030           player->jy = player->last_jy = jy;
4031
4032           break;
4033         }
4034       }
4035     }
4036   }
4037
4038   if (tape.playing)
4039   {
4040     /* when playing a tape, eliminate all players who do not participate */
4041
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043     {
4044       if (stored_player[i].active && !tape.player_participates[i])
4045       {
4046         struct PlayerInfo *player = &stored_player[i];
4047         int jx = player->jx, jy = player->jy;
4048
4049         player->active = FALSE;
4050         StorePlayer[jx][jy] = 0;
4051         Feld[jx][jy] = EL_EMPTY;
4052       }
4053     }
4054   }
4055   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4056   {
4057     /* when in single player mode, eliminate all but the first active player */
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060     {
4061       if (stored_player[i].active)
4062       {
4063         for (j = i + 1; j < MAX_PLAYERS; j++)
4064         {
4065           if (stored_player[j].active)
4066           {
4067             struct PlayerInfo *player = &stored_player[j];
4068             int jx = player->jx, jy = player->jy;
4069
4070             player->active = FALSE;
4071             player->present = FALSE;
4072
4073             StorePlayer[jx][jy] = 0;
4074             Feld[jx][jy] = EL_EMPTY;
4075           }
4076         }
4077       }
4078     }
4079   }
4080
4081   /* when recording the game, store which players take part in the game */
4082   if (tape.recording)
4083   {
4084     for (i = 0; i < MAX_PLAYERS; i++)
4085       if (stored_player[i].active)
4086         tape.player_participates[i] = TRUE;
4087   }
4088
4089   if (options.debug)
4090   {
4091     for (i = 0; i < MAX_PLAYERS; i++)
4092     {
4093       struct PlayerInfo *player = &stored_player[i];
4094
4095       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4096              i+1,
4097              player->present,
4098              player->connected,
4099              player->active);
4100       if (local_player == player)
4101         printf("Player  %d is local player.\n", i+1);
4102     }
4103   }
4104
4105   if (BorderElement == EL_EMPTY)
4106   {
4107     SBX_Left = 0;
4108     SBX_Right = lev_fieldx - SCR_FIELDX;
4109     SBY_Upper = 0;
4110     SBY_Lower = lev_fieldy - SCR_FIELDY;
4111   }
4112   else
4113   {
4114     SBX_Left = -1;
4115     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4116     SBY_Upper = -1;
4117     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4118   }
4119
4120   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4121     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4122
4123   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4124     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4125
4126   /* if local player not found, look for custom element that might create
4127      the player (make some assumptions about the right custom element) */
4128   if (!local_player->present)
4129   {
4130     int start_x = 0, start_y = 0;
4131     int found_rating = 0;
4132     int found_element = EL_UNDEFINED;
4133     int player_nr = local_player->index_nr;
4134
4135     SCAN_PLAYFIELD(x, y)
4136     {
4137       int element = Feld[x][y];
4138       int content;
4139       int xx, yy;
4140       boolean is_player;
4141
4142       if (level.use_start_element[player_nr] &&
4143           level.start_element[player_nr] == element &&
4144           found_rating < 4)
4145       {
4146         start_x = x;
4147         start_y = y;
4148
4149         found_rating = 4;
4150         found_element = element;
4151       }
4152
4153       if (!IS_CUSTOM_ELEMENT(element))
4154         continue;
4155
4156       if (CAN_CHANGE(element))
4157       {
4158         for (i = 0; i < element_info[element].num_change_pages; i++)
4159         {
4160           /* check for player created from custom element as single target */
4161           content = element_info[element].change_page[i].target_element;
4162           is_player = ELEM_IS_PLAYER(content);
4163
4164           if (is_player && (found_rating < 3 ||
4165                             (found_rating == 3 && element < found_element)))
4166           {
4167             start_x = x;
4168             start_y = y;
4169
4170             found_rating = 3;
4171             found_element = element;
4172           }
4173         }
4174       }
4175
4176       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4177       {
4178         /* check for player created from custom element as explosion content */
4179         content = element_info[element].content.e[xx][yy];
4180         is_player = ELEM_IS_PLAYER(content);
4181
4182         if (is_player && (found_rating < 2 ||
4183                           (found_rating == 2 && element < found_element)))
4184         {
4185           start_x = x + xx - 1;
4186           start_y = y + yy - 1;
4187
4188           found_rating = 2;
4189           found_element = element;
4190         }
4191
4192         if (!CAN_CHANGE(element))
4193           continue;
4194
4195         for (i = 0; i < element_info[element].num_change_pages; i++)
4196         {
4197           /* check for player created from custom element as extended target */
4198           content =
4199             element_info[element].change_page[i].target_content.e[xx][yy];
4200
4201           is_player = ELEM_IS_PLAYER(content);
4202
4203           if (is_player && (found_rating < 1 ||
4204                             (found_rating == 1 && element < found_element)))
4205           {
4206             start_x = x + xx - 1;
4207             start_y = y + yy - 1;
4208
4209             found_rating = 1;
4210             found_element = element;
4211           }
4212         }
4213       }
4214     }
4215
4216     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4217                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4218                 start_x - MIDPOSX);
4219
4220     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4221                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4222                 start_y - MIDPOSY);
4223   }
4224   else
4225   {
4226     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4227                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4228                 local_player->jx - MIDPOSX);
4229
4230     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4231                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4232                 local_player->jy - MIDPOSY);
4233   }
4234
4235 #if 0
4236   /* do not use PLAYING mask for fading out from main screen */
4237   game_status = GAME_MODE_MAIN;
4238 #endif
4239
4240   StopAnimation();
4241
4242   if (!game.restart_level)
4243     CloseDoor(DOOR_CLOSE_1);
4244
4245 #if 1
4246   if (level_editor_test_game)
4247     FadeSkipNextFadeIn();
4248   else
4249     FadeSetEnterScreen();
4250 #else
4251   if (level_editor_test_game)
4252     fading = fading_none;
4253   else
4254     fading = menu.destination;
4255 #endif
4256
4257 #if 1
4258   FadeOut(REDRAW_FIELD);
4259 #else
4260   if (do_fading)
4261     FadeOut(REDRAW_FIELD);
4262 #endif
4263
4264 #if 0
4265   game_status = GAME_MODE_PLAYING;
4266 #endif
4267
4268   /* !!! FIX THIS (START) !!! */
4269   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4270   {
4271     InitGameEngine_EM();
4272
4273     /* blit playfield from scroll buffer to normal back buffer for fading in */
4274     BlitScreenToBitmap_EM(backbuffer);
4275   }
4276   else
4277   {
4278     DrawLevel();
4279     DrawAllPlayers();
4280
4281     /* after drawing the level, correct some elements */
4282     if (game.timegate_time_left == 0)
4283       CloseAllOpenTimegates();
4284
4285     /* blit playfield from scroll buffer to normal back buffer for fading in */
4286     if (setup.soft_scrolling)
4287       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4288
4289     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4290   }
4291   /* !!! FIX THIS (END) !!! */
4292
4293 #if 1
4294   FadeIn(REDRAW_FIELD);
4295 #else
4296   if (do_fading)
4297     FadeIn(REDRAW_FIELD);
4298
4299   BackToFront();
4300 #endif
4301
4302   if (!game.restart_level)
4303   {
4304     /* copy default game door content to main double buffer */
4305     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4306                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4307   }
4308
4309   SetPanelBackground();
4310   SetDrawBackgroundMask(REDRAW_DOOR_1);
4311
4312 #if 1
4313   UpdateAndDisplayGameControlValues();
4314 #else
4315   UpdateGameDoorValues();
4316   DrawGameDoorValues();
4317 #endif
4318
4319   if (!game.restart_level)
4320   {
4321     UnmapGameButtons();
4322     UnmapTapeButtons();
4323     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4324     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4325     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4326     MapGameButtons();
4327     MapTapeButtons();
4328
4329     /* copy actual game door content to door double buffer for OpenDoor() */
4330     BlitBitmap(drawto, bitmap_db_door,
4331                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4332
4333     OpenDoor(DOOR_OPEN_ALL);
4334
4335     PlaySound(SND_GAME_STARTING);
4336
4337     if (setup.sound_music)
4338       PlayLevelMusic();
4339
4340     KeyboardAutoRepeatOffUnlessAutoplay();
4341
4342     if (options.debug)
4343     {
4344       for (i = 0; i < MAX_PLAYERS; i++)
4345         printf("Player %d %sactive.\n",
4346                i + 1, (stored_player[i].active ? "" : "not "));
4347     }
4348   }
4349
4350 #if 1
4351   UnmapAllGadgets();
4352
4353   MapGameButtons();
4354   MapTapeButtons();
4355 #endif
4356
4357   game.restart_level = FALSE;
4358 }
4359
4360 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4361 {
4362   /* this is used for non-R'n'D game engines to update certain engine values */
4363
4364   /* needed to determine if sounds are played within the visible screen area */
4365   scroll_x = actual_scroll_x;
4366   scroll_y = actual_scroll_y;
4367 }
4368
4369 void InitMovDir(int x, int y)
4370 {
4371   int i, element = Feld[x][y];
4372   static int xy[4][2] =
4373   {
4374     {  0, +1 },
4375     { +1,  0 },
4376     {  0, -1 },
4377     { -1,  0 }
4378   };
4379   static int direction[3][4] =
4380   {
4381     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4382     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4383     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4384   };
4385
4386   switch (element)
4387   {
4388     case EL_BUG_RIGHT:
4389     case EL_BUG_UP:
4390     case EL_BUG_LEFT:
4391     case EL_BUG_DOWN:
4392       Feld[x][y] = EL_BUG;
4393       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4394       break;
4395
4396     case EL_SPACESHIP_RIGHT:
4397     case EL_SPACESHIP_UP:
4398     case EL_SPACESHIP_LEFT:
4399     case EL_SPACESHIP_DOWN:
4400       Feld[x][y] = EL_SPACESHIP;
4401       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4402       break;
4403
4404     case EL_BD_BUTTERFLY_RIGHT:
4405     case EL_BD_BUTTERFLY_UP:
4406     case EL_BD_BUTTERFLY_LEFT:
4407     case EL_BD_BUTTERFLY_DOWN:
4408       Feld[x][y] = EL_BD_BUTTERFLY;
4409       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4410       break;
4411
4412     case EL_BD_FIREFLY_RIGHT:
4413     case EL_BD_FIREFLY_UP:
4414     case EL_BD_FIREFLY_LEFT:
4415     case EL_BD_FIREFLY_DOWN:
4416       Feld[x][y] = EL_BD_FIREFLY;
4417       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4418       break;
4419
4420     case EL_PACMAN_RIGHT:
4421     case EL_PACMAN_UP:
4422     case EL_PACMAN_LEFT:
4423     case EL_PACMAN_DOWN:
4424       Feld[x][y] = EL_PACMAN;
4425       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4426       break;
4427
4428     case EL_YAMYAM_LEFT:
4429     case EL_YAMYAM_RIGHT:
4430     case EL_YAMYAM_UP:
4431     case EL_YAMYAM_DOWN:
4432       Feld[x][y] = EL_YAMYAM;
4433       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4434       break;
4435
4436     case EL_SP_SNIKSNAK:
4437       MovDir[x][y] = MV_UP;
4438       break;
4439
4440     case EL_SP_ELECTRON:
4441       MovDir[x][y] = MV_LEFT;
4442       break;
4443
4444     case EL_MOLE_LEFT:
4445     case EL_MOLE_RIGHT:
4446     case EL_MOLE_UP:
4447     case EL_MOLE_DOWN:
4448       Feld[x][y] = EL_MOLE;
4449       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4450       break;
4451
4452     default:
4453       if (IS_CUSTOM_ELEMENT(element))
4454       {
4455         struct ElementInfo *ei = &element_info[element];
4456         int move_direction_initial = ei->move_direction_initial;
4457         int move_pattern = ei->move_pattern;
4458
4459         if (move_direction_initial == MV_START_PREVIOUS)
4460         {
4461           if (MovDir[x][y] != MV_NONE)
4462             return;
4463
4464           move_direction_initial = MV_START_AUTOMATIC;
4465         }
4466
4467         if (move_direction_initial == MV_START_RANDOM)
4468           MovDir[x][y] = 1 << RND(4);
4469         else if (move_direction_initial & MV_ANY_DIRECTION)
4470           MovDir[x][y] = move_direction_initial;
4471         else if (move_pattern == MV_ALL_DIRECTIONS ||
4472                  move_pattern == MV_TURNING_LEFT ||
4473                  move_pattern == MV_TURNING_RIGHT ||
4474                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4475                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4476                  move_pattern == MV_TURNING_RANDOM)
4477           MovDir[x][y] = 1 << RND(4);
4478         else if (move_pattern == MV_HORIZONTAL)
4479           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4480         else if (move_pattern == MV_VERTICAL)
4481           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4482         else if (move_pattern & MV_ANY_DIRECTION)
4483           MovDir[x][y] = element_info[element].move_pattern;
4484         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4485                  move_pattern == MV_ALONG_RIGHT_SIDE)
4486         {
4487           /* use random direction as default start direction */
4488           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4489             MovDir[x][y] = 1 << RND(4);
4490
4491           for (i = 0; i < NUM_DIRECTIONS; i++)
4492           {
4493             int x1 = x + xy[i][0];
4494             int y1 = y + xy[i][1];
4495
4496             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4497             {
4498               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4499                 MovDir[x][y] = direction[0][i];
4500               else
4501                 MovDir[x][y] = direction[1][i];
4502
4503               break;
4504             }
4505           }
4506         }                
4507       }
4508       else
4509       {
4510         MovDir[x][y] = 1 << RND(4);
4511
4512         if (element != EL_BUG &&
4513             element != EL_SPACESHIP &&
4514             element != EL_BD_BUTTERFLY &&
4515             element != EL_BD_FIREFLY)
4516           break;
4517
4518         for (i = 0; i < NUM_DIRECTIONS; i++)
4519         {
4520           int x1 = x + xy[i][0];
4521           int y1 = y + xy[i][1];
4522
4523           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4524           {
4525             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4526             {
4527               MovDir[x][y] = direction[0][i];
4528               break;
4529             }
4530             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4531                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4532             {
4533               MovDir[x][y] = direction[1][i];
4534               break;
4535             }
4536           }
4537         }
4538       }
4539       break;
4540   }
4541
4542   GfxDir[x][y] = MovDir[x][y];
4543 }
4544
4545 void InitAmoebaNr(int x, int y)
4546 {
4547   int i;
4548   int group_nr = AmoebeNachbarNr(x, y);
4549
4550   if (group_nr == 0)
4551   {
4552     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4553     {
4554       if (AmoebaCnt[i] == 0)
4555       {
4556         group_nr = i;
4557         break;
4558       }
4559     }
4560   }
4561
4562   AmoebaNr[x][y] = group_nr;
4563   AmoebaCnt[group_nr]++;
4564   AmoebaCnt2[group_nr]++;
4565 }
4566
4567 static void PlayerWins(struct PlayerInfo *player)
4568 {
4569   player->LevelSolved = TRUE;
4570   player->GameOver = TRUE;
4571
4572   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4573                          level.native_em_level->lev->score : player->score);
4574
4575   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4576   player->LevelSolved_CountingScore = player->score_final;
4577 }
4578
4579 void GameWon()
4580 {
4581   static int time, time_final;
4582   static int score, score_final;
4583   static int game_over_delay_1 = 0;
4584   static int game_over_delay_2 = 0;
4585   int game_over_delay_value_1 = 50;
4586   int game_over_delay_value_2 = 50;
4587
4588   if (!local_player->LevelSolved_GameWon)
4589   {
4590     int i;
4591
4592     /* do not start end game actions before the player stops moving (to exit) */
4593     if (local_player->MovPos)
4594       return;
4595
4596     local_player->LevelSolved_GameWon = TRUE;
4597     local_player->LevelSolved_SaveTape = tape.recording;
4598     local_player->LevelSolved_SaveScore = !tape.playing;
4599
4600     if (tape.auto_play)         /* tape might already be stopped here */
4601       tape.auto_play_level_solved = TRUE;
4602
4603 #if 1
4604     TapeStop();
4605 #endif
4606
4607     game_over_delay_1 = game_over_delay_value_1;
4608     game_over_delay_2 = game_over_delay_value_2;
4609
4610     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4611     score = score_final = local_player->score_final;
4612
4613     if (TimeLeft > 0)
4614     {
4615       time_final = 0;
4616       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4617     }
4618     else if (level.time == 0 && TimePlayed < 999)
4619     {
4620       time_final = 999;
4621       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4622     }
4623
4624     local_player->score_final = score_final;
4625
4626     if (level_editor_test_game)
4627     {
4628       time = time_final;
4629       score = score_final;
4630
4631 #if 1
4632       local_player->LevelSolved_CountingTime = time;
4633       local_player->LevelSolved_CountingScore = score;
4634
4635       game_panel_controls[GAME_PANEL_TIME].value = time;
4636       game_panel_controls[GAME_PANEL_SCORE].value = score;
4637
4638       DisplayGameControlValues();
4639 #else
4640       DrawGameValue_Time(time);
4641       DrawGameValue_Score(score);
4642 #endif
4643     }
4644
4645     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4646     {
4647       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4648       {
4649         /* close exit door after last player */
4650         if ((AllPlayersGone &&
4651              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4652               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4653               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4654             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4655             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4656         {
4657           int element = Feld[ExitX][ExitY];
4658
4659 #if 0
4660           if (element == EL_EM_EXIT_OPEN ||
4661               element == EL_EM_STEEL_EXIT_OPEN)
4662           {
4663             Bang(ExitX, ExitY);
4664           }
4665           else
4666 #endif
4667           {
4668             Feld[ExitX][ExitY] =
4669               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4670                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4671                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4672                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4673                EL_EM_STEEL_EXIT_CLOSING);
4674
4675             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4676           }
4677         }
4678
4679         /* player disappears */
4680         DrawLevelField(ExitX, ExitY);
4681       }
4682
4683       for (i = 0; i < MAX_PLAYERS; i++)
4684       {
4685         struct PlayerInfo *player = &stored_player[i];
4686
4687         if (player->present)
4688         {
4689           RemovePlayer(player);
4690
4691           /* player disappears */
4692           DrawLevelField(player->jx, player->jy);
4693         }
4694       }
4695     }
4696
4697     PlaySound(SND_GAME_WINNING);
4698   }
4699
4700   if (game_over_delay_1 > 0)
4701   {
4702     game_over_delay_1--;
4703
4704     return;
4705   }
4706
4707   if (time != time_final)
4708   {
4709     int time_to_go = ABS(time_final - time);
4710     int time_count_dir = (time < time_final ? +1 : -1);
4711     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4712
4713     time  += time_count_steps * time_count_dir;
4714     score += time_count_steps * level.score[SC_TIME_BONUS];
4715
4716 #if 1
4717     local_player->LevelSolved_CountingTime = time;
4718     local_player->LevelSolved_CountingScore = score;
4719
4720     game_panel_controls[GAME_PANEL_TIME].value = time;
4721     game_panel_controls[GAME_PANEL_SCORE].value = score;
4722
4723     DisplayGameControlValues();
4724 #else
4725     DrawGameValue_Time(time);
4726     DrawGameValue_Score(score);
4727 #endif
4728
4729     if (time == time_final)
4730       StopSound(SND_GAME_LEVELTIME_BONUS);
4731     else if (setup.sound_loops)
4732       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4733     else
4734       PlaySound(SND_GAME_LEVELTIME_BONUS);
4735
4736     return;
4737   }
4738
4739   local_player->LevelSolved_PanelOff = TRUE;
4740
4741   if (game_over_delay_2 > 0)
4742   {
4743     game_over_delay_2--;
4744
4745     return;
4746   }
4747
4748 #if 1
4749   GameEnd();
4750 #endif
4751 }
4752
4753 void GameEnd()
4754 {
4755   int hi_pos;
4756   boolean raise_level = FALSE;
4757
4758   local_player->LevelSolved_GameEnd = TRUE;
4759
4760   CloseDoor(DOOR_CLOSE_1);
4761
4762   if (local_player->LevelSolved_SaveTape)
4763   {
4764 #if 0
4765     TapeStop();
4766 #endif
4767
4768 #if 1
4769     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4770 #else
4771     SaveTape(tape.level_nr);            /* ask to save tape */
4772 #endif
4773   }
4774
4775   if (level_editor_test_game)
4776   {
4777     game_status = GAME_MODE_MAIN;
4778
4779 #if 1
4780     DrawAndFadeInMainMenu(REDRAW_FIELD);
4781 #else
4782     DrawMainMenu();
4783 #endif
4784
4785     return;
4786   }
4787
4788   if (!local_player->LevelSolved_SaveScore)
4789   {
4790 #if 1
4791     FadeOut(REDRAW_FIELD);
4792 #endif
4793
4794     game_status = GAME_MODE_MAIN;
4795
4796     DrawAndFadeInMainMenu(REDRAW_FIELD);
4797
4798     return;
4799   }
4800
4801   if (level_nr == leveldir_current->handicap_level)
4802   {
4803     leveldir_current->handicap_level++;
4804     SaveLevelSetup_SeriesInfo();
4805   }
4806
4807   if (level_nr < leveldir_current->last_level)
4808     raise_level = TRUE;                 /* advance to next level */
4809
4810   if ((hi_pos = NewHiScore()) >= 0) 
4811   {
4812     game_status = GAME_MODE_SCORES;
4813
4814     DrawHallOfFame(hi_pos);
4815
4816     if (raise_level)
4817     {
4818       level_nr++;
4819       TapeErase();
4820     }
4821   }
4822   else
4823   {
4824 #if 1
4825     FadeOut(REDRAW_FIELD);
4826 #endif
4827
4828     game_status = GAME_MODE_MAIN;
4829
4830     if (raise_level)
4831     {
4832       level_nr++;
4833       TapeErase();
4834     }
4835
4836     DrawAndFadeInMainMenu(REDRAW_FIELD);
4837   }
4838 }
4839
4840 int NewHiScore()
4841 {
4842   int k, l;
4843   int position = -1;
4844
4845   LoadScore(level_nr);
4846
4847   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4848       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4849     return -1;
4850
4851   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4852   {
4853     if (local_player->score_final > highscore[k].Score)
4854     {
4855       /* player has made it to the hall of fame */
4856
4857       if (k < MAX_SCORE_ENTRIES - 1)
4858       {
4859         int m = MAX_SCORE_ENTRIES - 1;
4860
4861 #ifdef ONE_PER_NAME
4862         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4863           if (strEqual(setup.player_name, highscore[l].Name))
4864             m = l;
4865         if (m == k)     /* player's new highscore overwrites his old one */
4866           goto put_into_list;
4867 #endif
4868
4869         for (l = m; l > k; l--)
4870         {
4871           strcpy(highscore[l].Name, highscore[l - 1].Name);
4872           highscore[l].Score = highscore[l - 1].Score;
4873         }
4874       }
4875
4876 #ifdef ONE_PER_NAME
4877       put_into_list:
4878 #endif
4879       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4880       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4881       highscore[k].Score = local_player->score_final; 
4882       position = k;
4883       break;
4884     }
4885
4886 #ifdef ONE_PER_NAME
4887     else if (!strncmp(setup.player_name, highscore[k].Name,
4888                       MAX_PLAYER_NAME_LEN))
4889       break;    /* player already there with a higher score */
4890 #endif
4891
4892   }
4893
4894   if (position >= 0) 
4895     SaveScore(level_nr);
4896
4897   return position;
4898 }
4899
4900 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4901 {
4902   int element = Feld[x][y];
4903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4905   int horiz_move = (dx != 0);
4906   int sign = (horiz_move ? dx : dy);
4907   int step = sign * element_info[element].move_stepsize;
4908
4909   /* special values for move stepsize for spring and things on conveyor belt */
4910   if (horiz_move)
4911   {
4912     if (CAN_FALL(element) &&
4913         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4914       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4915     else if (element == EL_SPRING)
4916       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4917   }
4918
4919   return step;
4920 }
4921
4922 inline static int getElementMoveStepsize(int x, int y)
4923 {
4924   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4925 }
4926
4927 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4928 {
4929   if (player->GfxAction != action || player->GfxDir != dir)
4930   {
4931 #if 0
4932     printf("Player frame reset! (%d => %d, %d => %d)\n",
4933            player->GfxAction, action, player->GfxDir, dir);
4934 #endif
4935
4936     player->GfxAction = action;
4937     player->GfxDir = dir;
4938     player->Frame = 0;
4939     player->StepFrame = 0;
4940   }
4941 }
4942
4943 #if USE_GFX_RESET_GFX_ANIMATION
4944 static void ResetGfxFrame(int x, int y, boolean redraw)
4945 {
4946   int element = Feld[x][y];
4947   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4948   int last_gfx_frame = GfxFrame[x][y];
4949
4950   if (graphic_info[graphic].anim_global_sync)
4951     GfxFrame[x][y] = FrameCounter;
4952   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4953     GfxFrame[x][y] = CustomValue[x][y];
4954   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4955     GfxFrame[x][y] = element_info[element].collect_score;
4956   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4957     GfxFrame[x][y] = ChangeDelay[x][y];
4958
4959   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4960     DrawLevelGraphicAnimation(x, y, graphic);
4961 }
4962 #endif
4963
4964 static void ResetGfxAnimation(int x, int y)
4965 {
4966   GfxAction[x][y] = ACTION_DEFAULT;
4967   GfxDir[x][y] = MovDir[x][y];
4968   GfxFrame[x][y] = 0;
4969
4970 #if USE_GFX_RESET_GFX_ANIMATION
4971   ResetGfxFrame(x, y, FALSE);
4972 #endif
4973 }
4974
4975 static void ResetRandomAnimationValue(int x, int y)
4976 {
4977   GfxRandom[x][y] = INIT_GFX_RANDOM();
4978 }
4979
4980 void InitMovingField(int x, int y, int direction)
4981 {
4982   int element = Feld[x][y];
4983   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4984   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4985   int newx = x + dx;
4986   int newy = y + dy;
4987   boolean is_moving_before, is_moving_after;
4988 #if 0
4989   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4990 #endif
4991
4992   /* check if element was/is moving or being moved before/after mode change */
4993 #if 1
4994 #if 1
4995   is_moving_before = (WasJustMoving[x][y] != 0);
4996 #else
4997   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4998   is_moving_before = WasJustMoving[x][y];
4999 #endif
5000 #else
5001   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5002 #endif
5003   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5004
5005   /* reset animation only for moving elements which change direction of moving
5006      or which just started or stopped moving
5007      (else CEs with property "can move" / "not moving" are reset each frame) */
5008 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5009 #if 1
5010   if (is_moving_before != is_moving_after ||
5011       direction != MovDir[x][y])
5012     ResetGfxAnimation(x, y);
5013 #else
5014   if ((is_moving_before || is_moving_after) && !continues_moving)
5015     ResetGfxAnimation(x, y);
5016 #endif
5017 #else
5018   if (!continues_moving)
5019     ResetGfxAnimation(x, y);
5020 #endif
5021
5022   MovDir[x][y] = direction;
5023   GfxDir[x][y] = direction;
5024
5025 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5026   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5027                      direction == MV_DOWN && CAN_FALL(element) ?
5028                      ACTION_FALLING : ACTION_MOVING);
5029 #else
5030   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5031                      ACTION_FALLING : ACTION_MOVING);
5032 #endif
5033
5034   /* this is needed for CEs with property "can move" / "not moving" */
5035
5036   if (is_moving_after)
5037   {
5038     if (Feld[newx][newy] == EL_EMPTY)
5039       Feld[newx][newy] = EL_BLOCKED;
5040
5041     MovDir[newx][newy] = MovDir[x][y];
5042
5043 #if USE_NEW_CUSTOM_VALUE
5044     CustomValue[newx][newy] = CustomValue[x][y];
5045 #endif
5046
5047     GfxFrame[newx][newy] = GfxFrame[x][y];
5048     GfxRandom[newx][newy] = GfxRandom[x][y];
5049     GfxAction[newx][newy] = GfxAction[x][y];
5050     GfxDir[newx][newy] = GfxDir[x][y];
5051   }
5052 }
5053
5054 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5055 {
5056   int direction = MovDir[x][y];
5057   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5058   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5059
5060   *goes_to_x = newx;
5061   *goes_to_y = newy;
5062 }
5063
5064 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5065 {
5066   int oldx = x, oldy = y;
5067   int direction = MovDir[x][y];
5068
5069   if (direction == MV_LEFT)
5070     oldx++;
5071   else if (direction == MV_RIGHT)
5072     oldx--;
5073   else if (direction == MV_UP)
5074     oldy++;
5075   else if (direction == MV_DOWN)
5076     oldy--;
5077
5078   *comes_from_x = oldx;
5079   *comes_from_y = oldy;
5080 }
5081
5082 int MovingOrBlocked2Element(int x, int y)
5083 {
5084   int element = Feld[x][y];
5085
5086   if (element == EL_BLOCKED)
5087   {
5088     int oldx, oldy;
5089
5090     Blocked2Moving(x, y, &oldx, &oldy);
5091     return Feld[oldx][oldy];
5092   }
5093   else
5094     return element;
5095 }
5096
5097 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5098 {
5099   /* like MovingOrBlocked2Element(), but if element is moving
5100      and (x,y) is the field the moving element is just leaving,
5101      return EL_BLOCKED instead of the element value */
5102   int element = Feld[x][y];
5103
5104   if (IS_MOVING(x, y))
5105   {
5106     if (element == EL_BLOCKED)
5107     {
5108       int oldx, oldy;
5109
5110       Blocked2Moving(x, y, &oldx, &oldy);
5111       return Feld[oldx][oldy];
5112     }
5113     else
5114       return EL_BLOCKED;
5115   }
5116   else
5117     return element;
5118 }
5119
5120 static void RemoveField(int x, int y)
5121 {
5122   Feld[x][y] = EL_EMPTY;
5123
5124   MovPos[x][y] = 0;
5125   MovDir[x][y] = 0;
5126   MovDelay[x][y] = 0;
5127
5128 #if USE_NEW_CUSTOM_VALUE
5129   CustomValue[x][y] = 0;
5130 #endif
5131
5132   AmoebaNr[x][y] = 0;
5133   ChangeDelay[x][y] = 0;
5134   ChangePage[x][y] = -1;
5135   Pushed[x][y] = FALSE;
5136
5137 #if 0
5138   ExplodeField[x][y] = EX_TYPE_NONE;
5139 #endif
5140
5141   GfxElement[x][y] = EL_UNDEFINED;
5142   GfxAction[x][y] = ACTION_DEFAULT;
5143   GfxDir[x][y] = MV_NONE;
5144 #if 0
5145   /* !!! this would prevent the removed tile from being redrawn !!! */
5146   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5147 #endif
5148 }
5149
5150 void RemoveMovingField(int x, int y)
5151 {
5152   int oldx = x, oldy = y, newx = x, newy = y;
5153   int element = Feld[x][y];
5154   int next_element = EL_UNDEFINED;
5155
5156   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5157     return;
5158
5159   if (IS_MOVING(x, y))
5160   {
5161     Moving2Blocked(x, y, &newx, &newy);
5162
5163     if (Feld[newx][newy] != EL_BLOCKED)
5164     {
5165       /* element is moving, but target field is not free (blocked), but
5166          already occupied by something different (example: acid pool);
5167          in this case, only remove the moving field, but not the target */
5168
5169       RemoveField(oldx, oldy);
5170
5171       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5172
5173       TEST_DrawLevelField(oldx, oldy);
5174
5175       return;
5176     }
5177   }
5178   else if (element == EL_BLOCKED)
5179   {
5180     Blocked2Moving(x, y, &oldx, &oldy);
5181     if (!IS_MOVING(oldx, oldy))
5182       return;
5183   }
5184
5185   if (element == EL_BLOCKED &&
5186       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5187        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5188        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5189        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5190        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5191        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5192     next_element = get_next_element(Feld[oldx][oldy]);
5193
5194   RemoveField(oldx, oldy);
5195   RemoveField(newx, newy);
5196
5197   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5198
5199   if (next_element != EL_UNDEFINED)
5200     Feld[oldx][oldy] = next_element;
5201
5202   TEST_DrawLevelField(oldx, oldy);
5203   TEST_DrawLevelField(newx, newy);
5204 }
5205
5206 void DrawDynamite(int x, int y)
5207 {
5208   int sx = SCREENX(x), sy = SCREENY(y);
5209   int graphic = el2img(Feld[x][y]);
5210   int frame;
5211
5212   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5213     return;
5214
5215   if (IS_WALKABLE_INSIDE(Back[x][y]))
5216     return;
5217
5218   if (Back[x][y])
5219     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5220   else if (Store[x][y])
5221     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5222
5223   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5224
5225   if (Back[x][y] || Store[x][y])
5226     DrawGraphicThruMask(sx, sy, graphic, frame);
5227   else
5228     DrawGraphic(sx, sy, graphic, frame);
5229 }
5230
5231 void CheckDynamite(int x, int y)
5232 {
5233   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5234   {
5235     MovDelay[x][y]--;
5236
5237     if (MovDelay[x][y] != 0)
5238     {
5239       DrawDynamite(x, y);
5240       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5241
5242       return;
5243     }
5244   }
5245
5246   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5247
5248   Bang(x, y);
5249 }
5250
5251 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5252 {
5253   boolean num_checked_players = 0;
5254   int i;
5255
5256   for (i = 0; i < MAX_PLAYERS; i++)
5257   {
5258     if (stored_player[i].active)
5259     {
5260       int sx = stored_player[i].jx;
5261       int sy = stored_player[i].jy;
5262
5263       if (num_checked_players == 0)
5264       {
5265         *sx1 = *sx2 = sx;
5266         *sy1 = *sy2 = sy;
5267       }
5268       else
5269       {
5270         *sx1 = MIN(*sx1, sx);
5271         *sy1 = MIN(*sy1, sy);
5272         *sx2 = MAX(*sx2, sx);
5273         *sy2 = MAX(*sy2, sy);
5274       }
5275
5276       num_checked_players++;
5277     }
5278   }
5279 }
5280
5281 static boolean checkIfAllPlayersFitToScreen_RND()
5282 {
5283   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5284
5285   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5286
5287   return (sx2 - sx1 < SCR_FIELDX &&
5288           sy2 - sy1 < SCR_FIELDY);
5289 }
5290
5291 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5292 {
5293   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5294
5295   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5296
5297   *sx = (sx1 + sx2) / 2;
5298   *sy = (sy1 + sy2) / 2;
5299 }
5300
5301 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5302                         boolean center_screen, boolean quick_relocation)
5303 {
5304   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5305   boolean no_delay = (tape.warp_forward);
5306   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5307   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5308
5309   if (quick_relocation)
5310   {
5311     int offset = game.scroll_delay_value;
5312
5313     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5314     {
5315       if (!level.shifted_relocation || center_screen)
5316       {
5317         /* quick relocation (without scrolling), with centering of screen */
5318
5319         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5320                     x > SBX_Right + MIDPOSX ? SBX_Right :
5321                     x - MIDPOSX);
5322
5323         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5324                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5325                     y - MIDPOSY);
5326       }
5327       else
5328       {
5329         /* quick relocation (without scrolling), but do not center screen */
5330
5331         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5332                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5333                                old_x - MIDPOSX);
5334
5335         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5336                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5337                                old_y - MIDPOSY);
5338
5339         int offset_x = x + (scroll_x - center_scroll_x);
5340         int offset_y = y + (scroll_y - center_scroll_y);
5341
5342         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5343                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5344                     offset_x - MIDPOSX);
5345
5346         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5347                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5348                     offset_y - MIDPOSY);
5349       }
5350     }
5351     else
5352     {
5353       /* quick relocation (without scrolling), inside visible screen area */
5354
5355       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5356           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5357         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5358
5359       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5360           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5361         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5362
5363       /* don't scroll over playfield boundaries */
5364       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5365         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5366
5367       /* don't scroll over playfield boundaries */
5368       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5369         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5370     }
5371
5372     RedrawPlayfield(TRUE, 0,0,0,0);
5373   }
5374   else
5375   {
5376 #if 1
5377     int scroll_xx, scroll_yy;
5378
5379     if (!level.shifted_relocation || center_screen)
5380     {
5381       /* visible relocation (with scrolling), with centering of screen */
5382
5383       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5384                    x > SBX_Right + MIDPOSX ? SBX_Right :
5385                    x - MIDPOSX);
5386
5387       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5388                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5389                    y - MIDPOSY);
5390     }
5391     else
5392     {
5393       /* visible relocation (with scrolling), but do not center screen */
5394
5395       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5396                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5397                              old_x - MIDPOSX);
5398
5399       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5400                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5401                              old_y - MIDPOSY);
5402
5403       int offset_x = x + (scroll_x - center_scroll_x);
5404       int offset_y = y + (scroll_y - center_scroll_y);
5405
5406       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5407                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5408                    offset_x - MIDPOSX);
5409
5410       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5411                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5412                    offset_y - MIDPOSY);
5413     }
5414
5415 #else
5416
5417     /* visible relocation (with scrolling), with centering of screen */
5418
5419     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5420                      x > SBX_Right + MIDPOSX ? SBX_Right :
5421                      x - MIDPOSX);
5422
5423     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5424                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5425                      y - MIDPOSY);
5426 #endif
5427
5428     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5429
5430     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5431     {
5432       int dx = 0, dy = 0;
5433       int fx = FX, fy = FY;
5434
5435       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5436       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5437
5438       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5439         break;
5440
5441       scroll_x -= dx;
5442       scroll_y -= dy;
5443
5444       fx += dx * TILEX / 2;
5445       fy += dy * TILEY / 2;
5446
5447       ScrollLevel(dx, dy);
5448       DrawAllPlayers();
5449
5450       /* scroll in two steps of half tile size to make things smoother */
5451       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5452       FlushDisplay();
5453       Delay(wait_delay_value);
5454
5455       /* scroll second step to align at full tile size */
5456       BackToFront();
5457       Delay(wait_delay_value);
5458     }
5459
5460     DrawAllPlayers();
5461     BackToFront();
5462     Delay(wait_delay_value);
5463   }
5464 }
5465
5466 void RelocatePlayer(int jx, int jy, int el_player_raw)
5467 {
5468   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5469   int player_nr = GET_PLAYER_NR(el_player);
5470   struct PlayerInfo *player = &stored_player[player_nr];
5471   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5472   boolean no_delay = (tape.warp_forward);
5473   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5474   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5475   int old_jx = player->jx;
5476   int old_jy = player->jy;
5477   int old_element = Feld[old_jx][old_jy];
5478   int element = Feld[jx][jy];
5479   boolean player_relocated = (old_jx != jx || old_jy != jy);
5480
5481   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5482   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5483   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5484   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5485   int leave_side_horiz = move_dir_horiz;
5486   int leave_side_vert  = move_dir_vert;
5487   int enter_side = enter_side_horiz | enter_side_vert;
5488   int leave_side = leave_side_horiz | leave_side_vert;
5489
5490   if (player->GameOver)         /* do not reanimate dead player */
5491     return;
5492
5493   if (!player_relocated)        /* no need to relocate the player */
5494     return;
5495
5496   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5497   {
5498     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5499     DrawLevelField(jx, jy);
5500   }
5501
5502   if (player->present)
5503   {
5504     while (player->MovPos)
5505     {
5506       ScrollPlayer(player, SCROLL_GO_ON);
5507       ScrollScreen(NULL, SCROLL_GO_ON);
5508
5509       AdvanceFrameAndPlayerCounters(player->index_nr);
5510
5511       DrawPlayer(player);
5512
5513       BackToFront();
5514       Delay(wait_delay_value);
5515     }
5516
5517     DrawPlayer(player);         /* needed here only to cleanup last field */
5518     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5519
5520     player->is_moving = FALSE;
5521   }
5522
5523   if (IS_CUSTOM_ELEMENT(old_element))
5524     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5525                                CE_LEFT_BY_PLAYER,
5526                                player->index_bit, leave_side);
5527
5528   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5529                                       CE_PLAYER_LEAVES_X,
5530                                       player->index_bit, leave_side);
5531
5532   Feld[jx][jy] = el_player;
5533   InitPlayerField(jx, jy, el_player, TRUE);
5534
5535   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5536   {
5537     Feld[jx][jy] = element;
5538     InitField(jx, jy, FALSE);
5539   }
5540
5541   /* only visually relocate centered player */
5542   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5543                      FALSE, level.instant_relocation);
5544
5545   TestIfPlayerTouchesBadThing(jx, jy);
5546   TestIfPlayerTouchesCustomElement(jx, jy);
5547
5548   if (IS_CUSTOM_ELEMENT(element))
5549     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5550                                player->index_bit, enter_side);
5551
5552   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5553                                       player->index_bit, enter_side);
5554 }
5555
5556 void Explode(int ex, int ey, int phase, int mode)
5557 {
5558   int x, y;
5559   int last_phase;
5560   int border_element;
5561
5562   /* !!! eliminate this variable !!! */
5563   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5564
5565   if (game.explosions_delayed)
5566   {
5567     ExplodeField[ex][ey] = mode;
5568     return;
5569   }
5570
5571   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5572   {
5573     int center_element = Feld[ex][ey];
5574     int artwork_element, explosion_element;     /* set these values later */
5575
5576 #if 0
5577     /* --- This is only really needed (and now handled) in "Impact()". --- */
5578     /* do not explode moving elements that left the explode field in time */
5579     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5580         center_element == EL_EMPTY &&
5581         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5582       return;
5583 #endif
5584
5585 #if 0
5586     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5587     if (mode == EX_TYPE_NORMAL ||
5588         mode == EX_TYPE_CENTER ||
5589         mode == EX_TYPE_CROSS)
5590       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5591 #endif
5592
5593     /* remove things displayed in background while burning dynamite */
5594     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5595       Back[ex][ey] = 0;
5596
5597     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5598     {
5599       /* put moving element to center field (and let it explode there) */
5600       center_element = MovingOrBlocked2Element(ex, ey);
5601       RemoveMovingField(ex, ey);
5602       Feld[ex][ey] = center_element;
5603     }
5604
5605     /* now "center_element" is finally determined -- set related values now */
5606     artwork_element = center_element;           /* for custom player artwork */
5607     explosion_element = center_element;         /* for custom player artwork */
5608
5609     if (IS_PLAYER(ex, ey))
5610     {
5611       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5612
5613       artwork_element = stored_player[player_nr].artwork_element;
5614
5615       if (level.use_explosion_element[player_nr])
5616       {
5617         explosion_element = level.explosion_element[player_nr];
5618         artwork_element = explosion_element;
5619       }
5620     }
5621
5622 #if 1
5623     if (mode == EX_TYPE_NORMAL ||
5624         mode == EX_TYPE_CENTER ||
5625         mode == EX_TYPE_CROSS)
5626       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5627 #endif
5628
5629     last_phase = element_info[explosion_element].explosion_delay + 1;
5630
5631     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5632     {
5633       int xx = x - ex + 1;
5634       int yy = y - ey + 1;
5635       int element;
5636
5637       if (!IN_LEV_FIELD(x, y) ||
5638           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5639           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5640         continue;
5641
5642       element = Feld[x][y];
5643
5644       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5645       {
5646         element = MovingOrBlocked2Element(x, y);
5647
5648         if (!IS_EXPLOSION_PROOF(element))
5649           RemoveMovingField(x, y);
5650       }
5651
5652       /* indestructible elements can only explode in center (but not flames) */
5653       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5654                                            mode == EX_TYPE_BORDER)) ||
5655           element == EL_FLAMES)
5656         continue;
5657
5658       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5659          behaviour, for example when touching a yamyam that explodes to rocks
5660          with active deadly shield, a rock is created under the player !!! */
5661       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5662 #if 0
5663       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5664           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5665            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5666 #else
5667       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5668 #endif
5669       {
5670         if (IS_ACTIVE_BOMB(element))
5671         {
5672           /* re-activate things under the bomb like gate or penguin */
5673           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5674           Back[x][y] = 0;
5675         }
5676
5677         continue;
5678       }
5679
5680       /* save walkable background elements while explosion on same tile */
5681       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5682           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5683         Back[x][y] = element;
5684
5685       /* ignite explodable elements reached by other explosion */
5686       if (element == EL_EXPLOSION)
5687         element = Store2[x][y];
5688
5689       if (AmoebaNr[x][y] &&
5690           (element == EL_AMOEBA_FULL ||
5691            element == EL_BD_AMOEBA ||
5692            element == EL_AMOEBA_GROWING))
5693       {
5694         AmoebaCnt[AmoebaNr[x][y]]--;
5695         AmoebaCnt2[AmoebaNr[x][y]]--;
5696       }
5697
5698       RemoveField(x, y);
5699
5700       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5701       {
5702         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5703
5704         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5705
5706         if (PLAYERINFO(ex, ey)->use_murphy)
5707           Store[x][y] = EL_EMPTY;
5708       }
5709
5710       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5711          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5712       else if (ELEM_IS_PLAYER(center_element))
5713         Store[x][y] = EL_EMPTY;
5714       else if (center_element == EL_YAMYAM)
5715         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5716       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5717         Store[x][y] = element_info[center_element].content.e[xx][yy];
5718 #if 1
5719       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5720          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5721          otherwise) -- FIX THIS !!! */
5722       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5723         Store[x][y] = element_info[element].content.e[1][1];
5724 #else
5725       else if (!CAN_EXPLODE(element))
5726         Store[x][y] = element_info[element].content.e[1][1];
5727 #endif
5728       else
5729         Store[x][y] = EL_EMPTY;
5730
5731       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5732           center_element == EL_AMOEBA_TO_DIAMOND)
5733         Store2[x][y] = element;
5734
5735       Feld[x][y] = EL_EXPLOSION;
5736       GfxElement[x][y] = artwork_element;
5737
5738       ExplodePhase[x][y] = 1;
5739       ExplodeDelay[x][y] = last_phase;
5740
5741       Stop[x][y] = TRUE;
5742     }
5743
5744     if (center_element == EL_YAMYAM)
5745       game.yamyam_content_nr =
5746         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5747
5748     return;
5749   }
5750
5751   if (Stop[ex][ey])
5752     return;
5753
5754   x = ex;
5755   y = ey;
5756
5757   if (phase == 1)
5758     GfxFrame[x][y] = 0;         /* restart explosion animation */
5759
5760   last_phase = ExplodeDelay[x][y];
5761
5762   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5763
5764 #ifdef DEBUG
5765
5766   /* activate this even in non-DEBUG version until cause for crash in
5767      getGraphicAnimationFrame() (see below) is found and eliminated */
5768
5769 #endif
5770 #if 1
5771
5772 #if 1
5773   /* this can happen if the player leaves an explosion just in time */
5774   if (GfxElement[x][y] == EL_UNDEFINED)
5775     GfxElement[x][y] = EL_EMPTY;
5776 #else
5777   if (GfxElement[x][y] == EL_UNDEFINED)
5778   {
5779     printf("\n\n");
5780     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5781     printf("Explode(): This should never happen!\n");
5782     printf("\n\n");
5783
5784     GfxElement[x][y] = EL_EMPTY;
5785   }
5786 #endif
5787
5788 #endif
5789
5790   border_element = Store2[x][y];
5791   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5792     border_element = StorePlayer[x][y];
5793
5794   if (phase == element_info[border_element].ignition_delay ||
5795       phase == last_phase)
5796   {
5797     boolean border_explosion = FALSE;
5798
5799     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5800         !PLAYER_EXPLOSION_PROTECTED(x, y))
5801     {
5802       KillPlayerUnlessExplosionProtected(x, y);
5803       border_explosion = TRUE;
5804     }
5805     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5806     {
5807       Feld[x][y] = Store2[x][y];
5808       Store2[x][y] = 0;
5809       Bang(x, y);
5810       border_explosion = TRUE;
5811     }
5812     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5813     {
5814       AmoebeUmwandeln(x, y);
5815       Store2[x][y] = 0;
5816       border_explosion = TRUE;
5817     }
5818
5819     /* if an element just explodes due to another explosion (chain-reaction),
5820        do not immediately end the new explosion when it was the last frame of
5821        the explosion (as it would be done in the following "if"-statement!) */
5822     if (border_explosion && phase == last_phase)
5823       return;
5824   }
5825
5826   if (phase == last_phase)
5827   {
5828     int element;
5829
5830     element = Feld[x][y] = Store[x][y];
5831     Store[x][y] = Store2[x][y] = 0;
5832     GfxElement[x][y] = EL_UNDEFINED;
5833
5834     /* player can escape from explosions and might therefore be still alive */
5835     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5836         element <= EL_PLAYER_IS_EXPLODING_4)
5837     {
5838       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5839       int explosion_element = EL_PLAYER_1 + player_nr;
5840       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5841       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5842
5843       if (level.use_explosion_element[player_nr])
5844         explosion_element = level.explosion_element[player_nr];
5845
5846       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5847                     element_info[explosion_element].content.e[xx][yy]);
5848     }
5849
5850     /* restore probably existing indestructible background element */
5851     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5852       element = Feld[x][y] = Back[x][y];
5853     Back[x][y] = 0;
5854
5855     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5856     GfxDir[x][y] = MV_NONE;
5857     ChangeDelay[x][y] = 0;
5858     ChangePage[x][y] = -1;
5859
5860 #if USE_NEW_CUSTOM_VALUE
5861     CustomValue[x][y] = 0;
5862 #endif
5863
5864     InitField_WithBug2(x, y, FALSE);
5865
5866     TEST_DrawLevelField(x, y);
5867
5868     TestIfElementTouchesCustomElement(x, y);
5869
5870     if (GFX_CRUMBLED(element))
5871       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5872
5873     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5874       StorePlayer[x][y] = 0;
5875
5876     if (ELEM_IS_PLAYER(element))
5877       RelocatePlayer(x, y, element);
5878   }
5879   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5880   {
5881     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5882     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5883
5884     if (phase == delay)
5885       TEST_DrawLevelFieldCrumbledSand(x, y);
5886
5887     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5888     {
5889       DrawLevelElement(x, y, Back[x][y]);
5890       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5891     }
5892     else if (IS_WALKABLE_UNDER(Back[x][y]))
5893     {
5894       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5895       DrawLevelElementThruMask(x, y, Back[x][y]);
5896     }
5897     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5898       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5899   }
5900 }
5901
5902 void DynaExplode(int ex, int ey)
5903 {
5904   int i, j;
5905   int dynabomb_element = Feld[ex][ey];
5906   int dynabomb_size = 1;
5907   boolean dynabomb_xl = FALSE;
5908   struct PlayerInfo *player;
5909   static int xy[4][2] =
5910   {
5911     { 0, -1 },
5912     { -1, 0 },
5913     { +1, 0 },
5914     { 0, +1 }
5915   };
5916
5917   if (IS_ACTIVE_BOMB(dynabomb_element))
5918   {
5919     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5920     dynabomb_size = player->dynabomb_size;
5921     dynabomb_xl = player->dynabomb_xl;
5922     player->dynabombs_left++;
5923   }
5924
5925   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5926
5927   for (i = 0; i < NUM_DIRECTIONS; i++)
5928   {
5929     for (j = 1; j <= dynabomb_size; j++)
5930     {
5931       int x = ex + j * xy[i][0];
5932       int y = ey + j * xy[i][1];
5933       int element;
5934
5935       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5936         break;
5937
5938       element = Feld[x][y];
5939
5940       /* do not restart explosions of fields with active bombs */
5941       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5942         continue;
5943
5944       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5945
5946       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5947           !IS_DIGGABLE(element) && !dynabomb_xl)
5948         break;
5949     }
5950   }
5951 }
5952
5953 void Bang(int x, int y)
5954 {
5955   int element = MovingOrBlocked2Element(x, y);
5956   int explosion_type = EX_TYPE_NORMAL;
5957
5958   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5959   {
5960     struct PlayerInfo *player = PLAYERINFO(x, y);
5961
5962 #if USE_FIX_CE_ACTION_WITH_PLAYER
5963     element = Feld[x][y] = player->initial_element;
5964 #else
5965     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5966                             player->element_nr);
5967 #endif
5968
5969     if (level.use_explosion_element[player->index_nr])
5970     {
5971       int explosion_element = level.explosion_element[player->index_nr];
5972
5973       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5974         explosion_type = EX_TYPE_CROSS;
5975       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5976         explosion_type = EX_TYPE_CENTER;
5977     }
5978   }
5979
5980   switch (element)
5981   {
5982     case EL_BUG:
5983     case EL_SPACESHIP:
5984     case EL_BD_BUTTERFLY:
5985     case EL_BD_FIREFLY:
5986     case EL_YAMYAM:
5987     case EL_DARK_YAMYAM:
5988     case EL_ROBOT:
5989     case EL_PACMAN:
5990     case EL_MOLE:
5991       RaiseScoreElement(element);
5992       break;
5993
5994     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5995     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5996     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5997     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5998     case EL_DYNABOMB_INCREASE_NUMBER:
5999     case EL_DYNABOMB_INCREASE_SIZE:
6000     case EL_DYNABOMB_INCREASE_POWER:
6001       explosion_type = EX_TYPE_DYNA;
6002       break;
6003
6004     case EL_DC_LANDMINE:
6005 #if 0
6006     case EL_EM_EXIT_OPEN:
6007     case EL_EM_STEEL_EXIT_OPEN:
6008 #endif
6009       explosion_type = EX_TYPE_CENTER;
6010       break;
6011
6012     case EL_PENGUIN:
6013     case EL_LAMP:
6014     case EL_LAMP_ACTIVE:
6015     case EL_AMOEBA_TO_DIAMOND:
6016       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6017         explosion_type = EX_TYPE_CENTER;
6018       break;
6019
6020     default:
6021       if (element_info[element].explosion_type == EXPLODES_CROSS)
6022         explosion_type = EX_TYPE_CROSS;
6023       else if (element_info[element].explosion_type == EXPLODES_1X1)
6024         explosion_type = EX_TYPE_CENTER;
6025       break;
6026   }
6027
6028   if (explosion_type == EX_TYPE_DYNA)
6029     DynaExplode(x, y);
6030   else
6031     Explode(x, y, EX_PHASE_START, explosion_type);
6032
6033   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6034 }
6035
6036 void SplashAcid(int x, int y)
6037 {
6038   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6039       (!IN_LEV_FIELD(x - 1, y - 2) ||
6040        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6041     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6042
6043   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6044       (!IN_LEV_FIELD(x + 1, y - 2) ||
6045        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6046     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6047
6048   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6049 }
6050
6051 static void InitBeltMovement()
6052 {
6053   static int belt_base_element[4] =
6054   {
6055     EL_CONVEYOR_BELT_1_LEFT,
6056     EL_CONVEYOR_BELT_2_LEFT,
6057     EL_CONVEYOR_BELT_3_LEFT,
6058     EL_CONVEYOR_BELT_4_LEFT
6059   };
6060   static int belt_base_active_element[4] =
6061   {
6062     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6063     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6064     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6065     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6066   };
6067
6068   int x, y, i, j;
6069
6070   /* set frame order for belt animation graphic according to belt direction */
6071   for (i = 0; i < NUM_BELTS; i++)
6072   {
6073     int belt_nr = i;
6074
6075     for (j = 0; j < NUM_BELT_PARTS; j++)
6076     {
6077       int element = belt_base_active_element[belt_nr] + j;
6078       int graphic_1 = el2img(element);
6079       int graphic_2 = el2panelimg(element);
6080
6081       if (game.belt_dir[i] == MV_LEFT)
6082       {
6083         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6085       }
6086       else
6087       {
6088         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6089         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6090       }
6091     }
6092   }
6093
6094   SCAN_PLAYFIELD(x, y)
6095   {
6096     int element = Feld[x][y];
6097
6098     for (i = 0; i < NUM_BELTS; i++)
6099     {
6100       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6101       {
6102         int e_belt_nr = getBeltNrFromBeltElement(element);
6103         int belt_nr = i;
6104
6105         if (e_belt_nr == belt_nr)
6106         {
6107           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6108
6109           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6110         }
6111       }
6112     }
6113   }
6114 }
6115
6116 static void ToggleBeltSwitch(int x, int y)
6117 {
6118   static int belt_base_element[4] =
6119   {
6120     EL_CONVEYOR_BELT_1_LEFT,
6121     EL_CONVEYOR_BELT_2_LEFT,
6122     EL_CONVEYOR_BELT_3_LEFT,
6123     EL_CONVEYOR_BELT_4_LEFT
6124   };
6125   static int belt_base_active_element[4] =
6126   {
6127     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6128     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6129     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6130     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6131   };
6132   static int belt_base_switch_element[4] =
6133   {
6134     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6135     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6136     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6137     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6138   };
6139   static int belt_move_dir[4] =
6140   {
6141     MV_LEFT,
6142     MV_NONE,
6143     MV_RIGHT,
6144     MV_NONE,
6145   };
6146
6147   int element = Feld[x][y];
6148   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6149   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6150   int belt_dir = belt_move_dir[belt_dir_nr];
6151   int xx, yy, i;
6152
6153   if (!IS_BELT_SWITCH(element))
6154     return;
6155
6156   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6157   game.belt_dir[belt_nr] = belt_dir;
6158
6159   if (belt_dir_nr == 3)
6160     belt_dir_nr = 1;
6161
6162   /* set frame order for belt animation graphic according to belt direction */
6163   for (i = 0; i < NUM_BELT_PARTS; i++)
6164   {
6165     int element = belt_base_active_element[belt_nr] + i;
6166     int graphic_1 = el2img(element);
6167     int graphic_2 = el2panelimg(element);
6168
6169     if (belt_dir == MV_LEFT)
6170     {
6171       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6172       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6173     }
6174     else
6175     {
6176       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6177       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6178     }
6179   }
6180
6181   SCAN_PLAYFIELD(xx, yy)
6182   {
6183     int element = Feld[xx][yy];
6184
6185     if (IS_BELT_SWITCH(element))
6186     {
6187       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6188
6189       if (e_belt_nr == belt_nr)
6190       {
6191         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6192         TEST_DrawLevelField(xx, yy);
6193       }
6194     }
6195     else if (IS_BELT(element) && belt_dir != MV_NONE)
6196     {
6197       int e_belt_nr = getBeltNrFromBeltElement(element);
6198
6199       if (e_belt_nr == belt_nr)
6200       {
6201         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6202
6203         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6204         TEST_DrawLevelField(xx, yy);
6205       }
6206     }
6207     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6208     {
6209       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6210
6211       if (e_belt_nr == belt_nr)
6212       {
6213         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6214
6215         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6216         TEST_DrawLevelField(xx, yy);
6217       }
6218     }
6219   }
6220 }
6221
6222 static void ToggleSwitchgateSwitch(int x, int y)
6223 {
6224   int xx, yy;
6225
6226   game.switchgate_pos = !game.switchgate_pos;
6227
6228   SCAN_PLAYFIELD(xx, yy)
6229   {
6230     int element = Feld[xx][yy];
6231
6232 #if !USE_BOTH_SWITCHGATE_SWITCHES
6233     if (element == EL_SWITCHGATE_SWITCH_UP ||
6234         element == EL_SWITCHGATE_SWITCH_DOWN)
6235     {
6236       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6237       TEST_DrawLevelField(xx, yy);
6238     }
6239     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6240              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6241     {
6242       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6243       TEST_DrawLevelField(xx, yy);
6244     }
6245 #else
6246     if (element == EL_SWITCHGATE_SWITCH_UP)
6247     {
6248       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6249       TEST_DrawLevelField(xx, yy);
6250     }
6251     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6252     {
6253       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6254       TEST_DrawLevelField(xx, yy);
6255     }
6256     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6257     {
6258       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6259       TEST_DrawLevelField(xx, yy);
6260     }
6261     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6262     {
6263       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6264       TEST_DrawLevelField(xx, yy);
6265     }
6266 #endif
6267     else if (element == EL_SWITCHGATE_OPEN ||
6268              element == EL_SWITCHGATE_OPENING)
6269     {
6270       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6271
6272       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6273     }
6274     else if (element == EL_SWITCHGATE_CLOSED ||
6275              element == EL_SWITCHGATE_CLOSING)
6276     {
6277       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6278
6279       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6280     }
6281   }
6282 }
6283
6284 static int getInvisibleActiveFromInvisibleElement(int element)
6285 {
6286   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6287           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6288           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6289           element);
6290 }
6291
6292 static int getInvisibleFromInvisibleActiveElement(int element)
6293 {
6294   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6295           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6296           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6297           element);
6298 }
6299
6300 static void RedrawAllLightSwitchesAndInvisibleElements()
6301 {
6302   int x, y;
6303
6304   SCAN_PLAYFIELD(x, y)
6305   {
6306     int element = Feld[x][y];
6307
6308     if (element == EL_LIGHT_SWITCH &&
6309         game.light_time_left > 0)
6310     {
6311       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6312       TEST_DrawLevelField(x, y);
6313     }
6314     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6315              game.light_time_left == 0)
6316     {
6317       Feld[x][y] = EL_LIGHT_SWITCH;
6318       TEST_DrawLevelField(x, y);
6319     }
6320     else if (element == EL_EMC_DRIPPER &&
6321              game.light_time_left > 0)
6322     {
6323       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6324       TEST_DrawLevelField(x, y);
6325     }
6326     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6327              game.light_time_left == 0)
6328     {
6329       Feld[x][y] = EL_EMC_DRIPPER;
6330       TEST_DrawLevelField(x, y);
6331     }
6332     else if (element == EL_INVISIBLE_STEELWALL ||
6333              element == EL_INVISIBLE_WALL ||
6334              element == EL_INVISIBLE_SAND)
6335     {
6336       if (game.light_time_left > 0)
6337         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6338
6339       TEST_DrawLevelField(x, y);
6340
6341       /* uncrumble neighbour fields, if needed */
6342       if (element == EL_INVISIBLE_SAND)
6343         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6344     }
6345     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6346              element == EL_INVISIBLE_WALL_ACTIVE ||
6347              element == EL_INVISIBLE_SAND_ACTIVE)
6348     {
6349       if (game.light_time_left == 0)
6350         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6351
6352       TEST_DrawLevelField(x, y);
6353
6354       /* re-crumble neighbour fields, if needed */
6355       if (element == EL_INVISIBLE_SAND)
6356         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6357     }
6358   }
6359 }
6360
6361 static void RedrawAllInvisibleElementsForLenses()
6362 {
6363   int x, y;
6364
6365   SCAN_PLAYFIELD(x, y)
6366   {
6367     int element = Feld[x][y];
6368
6369     if (element == EL_EMC_DRIPPER &&
6370         game.lenses_time_left > 0)
6371     {
6372       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6373       TEST_DrawLevelField(x, y);
6374     }
6375     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6376              game.lenses_time_left == 0)
6377     {
6378       Feld[x][y] = EL_EMC_DRIPPER;
6379       TEST_DrawLevelField(x, y);
6380     }
6381     else if (element == EL_INVISIBLE_STEELWALL ||
6382              element == EL_INVISIBLE_WALL ||
6383              element == EL_INVISIBLE_SAND)
6384     {
6385       if (game.lenses_time_left > 0)
6386         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6387
6388       TEST_DrawLevelField(x, y);
6389
6390       /* uncrumble neighbour fields, if needed */
6391       if (element == EL_INVISIBLE_SAND)
6392         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6393     }
6394     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6395              element == EL_INVISIBLE_WALL_ACTIVE ||
6396              element == EL_INVISIBLE_SAND_ACTIVE)
6397     {
6398       if (game.lenses_time_left == 0)
6399         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6400
6401       TEST_DrawLevelField(x, y);
6402
6403       /* re-crumble neighbour fields, if needed */
6404       if (element == EL_INVISIBLE_SAND)
6405         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6406     }
6407   }
6408 }
6409
6410 static void RedrawAllInvisibleElementsForMagnifier()
6411 {
6412   int x, y;
6413
6414   SCAN_PLAYFIELD(x, y)
6415   {
6416     int element = Feld[x][y];
6417
6418     if (element == EL_EMC_FAKE_GRASS &&
6419         game.magnify_time_left > 0)
6420     {
6421       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6422       TEST_DrawLevelField(x, y);
6423     }
6424     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6425              game.magnify_time_left == 0)
6426     {
6427       Feld[x][y] = EL_EMC_FAKE_GRASS;
6428       TEST_DrawLevelField(x, y);
6429     }
6430     else if (IS_GATE_GRAY(element) &&
6431              game.magnify_time_left > 0)
6432     {
6433       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6434                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6435                     IS_EM_GATE_GRAY(element) ?
6436                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6437                     IS_EMC_GATE_GRAY(element) ?
6438                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6439                     IS_DC_GATE_GRAY(element) ?
6440                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6441                     element);
6442       TEST_DrawLevelField(x, y);
6443     }
6444     else if (IS_GATE_GRAY_ACTIVE(element) &&
6445              game.magnify_time_left == 0)
6446     {
6447       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6448                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6449                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6450                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6451                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6452                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6453                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6454                     EL_DC_GATE_WHITE_GRAY :
6455                     element);
6456       TEST_DrawLevelField(x, y);
6457     }
6458   }
6459 }
6460
6461 static void ToggleLightSwitch(int x, int y)
6462 {
6463   int element = Feld[x][y];
6464
6465   game.light_time_left =
6466     (element == EL_LIGHT_SWITCH ?
6467      level.time_light * FRAMES_PER_SECOND : 0);
6468
6469   RedrawAllLightSwitchesAndInvisibleElements();
6470 }
6471
6472 static void ActivateTimegateSwitch(int x, int y)
6473 {
6474   int xx, yy;
6475
6476   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6477
6478   SCAN_PLAYFIELD(xx, yy)
6479   {
6480     int element = Feld[xx][yy];
6481
6482     if (element == EL_TIMEGATE_CLOSED ||
6483         element == EL_TIMEGATE_CLOSING)
6484     {
6485       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6486       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6487     }
6488
6489     /*
6490     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6491     {
6492       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6493       TEST_DrawLevelField(xx, yy);
6494     }
6495     */
6496
6497   }
6498
6499 #if 1
6500   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6501                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6502 #else
6503   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6504 #endif
6505 }
6506
6507 void Impact(int x, int y)
6508 {
6509   boolean last_line = (y == lev_fieldy - 1);
6510   boolean object_hit = FALSE;
6511   boolean impact = (last_line || object_hit);
6512   int element = Feld[x][y];
6513   int smashed = EL_STEELWALL;
6514
6515   if (!last_line)       /* check if element below was hit */
6516   {
6517     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6518       return;
6519
6520     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6521                                          MovDir[x][y + 1] != MV_DOWN ||
6522                                          MovPos[x][y + 1] <= TILEY / 2));
6523
6524     /* do not smash moving elements that left the smashed field in time */
6525     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6526         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6527       object_hit = FALSE;
6528
6529 #if USE_QUICKSAND_IMPACT_BUGFIX
6530     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6531     {
6532       RemoveMovingField(x, y + 1);
6533       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6534       Feld[x][y + 2] = EL_ROCK;
6535       TEST_DrawLevelField(x, y + 2);
6536
6537       object_hit = TRUE;
6538     }
6539
6540     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6541     {
6542       RemoveMovingField(x, y + 1);
6543       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6544       Feld[x][y + 2] = EL_ROCK;
6545       TEST_DrawLevelField(x, y + 2);
6546
6547       object_hit = TRUE;
6548     }
6549 #endif
6550
6551     if (object_hit)
6552       smashed = MovingOrBlocked2Element(x, y + 1);
6553
6554     impact = (last_line || object_hit);
6555   }
6556
6557   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6558   {
6559     SplashAcid(x, y + 1);
6560     return;
6561   }
6562
6563   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6564   /* only reset graphic animation if graphic really changes after impact */
6565   if (impact &&
6566       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6567   {
6568     ResetGfxAnimation(x, y);
6569     TEST_DrawLevelField(x, y);
6570   }
6571
6572   if (impact && CAN_EXPLODE_IMPACT(element))
6573   {
6574     Bang(x, y);
6575     return;
6576   }
6577   else if (impact && element == EL_PEARL &&
6578            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6579   {
6580     ResetGfxAnimation(x, y);
6581
6582     Feld[x][y] = EL_PEARL_BREAKING;
6583     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6584     return;
6585   }
6586   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6587   {
6588     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6589
6590     return;
6591   }
6592
6593   if (impact && element == EL_AMOEBA_DROP)
6594   {
6595     if (object_hit && IS_PLAYER(x, y + 1))
6596       KillPlayerUnlessEnemyProtected(x, y + 1);
6597     else if (object_hit && smashed == EL_PENGUIN)
6598       Bang(x, y + 1);
6599     else
6600     {
6601       Feld[x][y] = EL_AMOEBA_GROWING;
6602       Store[x][y] = EL_AMOEBA_WET;
6603
6604       ResetRandomAnimationValue(x, y);
6605     }
6606     return;
6607   }
6608
6609   if (object_hit)               /* check which object was hit */
6610   {
6611     if ((CAN_PASS_MAGIC_WALL(element) && 
6612          (smashed == EL_MAGIC_WALL ||
6613           smashed == EL_BD_MAGIC_WALL)) ||
6614         (CAN_PASS_DC_MAGIC_WALL(element) &&
6615          smashed == EL_DC_MAGIC_WALL))
6616     {
6617       int xx, yy;
6618       int activated_magic_wall =
6619         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6620          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6621          EL_DC_MAGIC_WALL_ACTIVE);
6622
6623       /* activate magic wall / mill */
6624       SCAN_PLAYFIELD(xx, yy)
6625       {
6626         if (Feld[xx][yy] == smashed)
6627           Feld[xx][yy] = activated_magic_wall;
6628       }
6629
6630       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6631       game.magic_wall_active = TRUE;
6632
6633       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6634                             SND_MAGIC_WALL_ACTIVATING :
6635                             smashed == EL_BD_MAGIC_WALL ?
6636                             SND_BD_MAGIC_WALL_ACTIVATING :
6637                             SND_DC_MAGIC_WALL_ACTIVATING));
6638     }
6639
6640     if (IS_PLAYER(x, y + 1))
6641     {
6642       if (CAN_SMASH_PLAYER(element))
6643       {
6644         KillPlayerUnlessEnemyProtected(x, y + 1);
6645         return;
6646       }
6647     }
6648     else if (smashed == EL_PENGUIN)
6649     {
6650       if (CAN_SMASH_PLAYER(element))
6651       {
6652         Bang(x, y + 1);
6653         return;
6654       }
6655     }
6656     else if (element == EL_BD_DIAMOND)
6657     {
6658       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6659       {
6660         Bang(x, y + 1);
6661         return;
6662       }
6663     }
6664     else if (((element == EL_SP_INFOTRON ||
6665                element == EL_SP_ZONK) &&
6666               (smashed == EL_SP_SNIKSNAK ||
6667                smashed == EL_SP_ELECTRON ||
6668                smashed == EL_SP_DISK_ORANGE)) ||
6669              (element == EL_SP_INFOTRON &&
6670               smashed == EL_SP_DISK_YELLOW))
6671     {
6672       Bang(x, y + 1);
6673       return;
6674     }
6675     else if (CAN_SMASH_EVERYTHING(element))
6676     {
6677       if (IS_CLASSIC_ENEMY(smashed) ||
6678           CAN_EXPLODE_SMASHED(smashed))
6679       {
6680         Bang(x, y + 1);
6681         return;
6682       }
6683       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6684       {
6685         if (smashed == EL_LAMP ||
6686             smashed == EL_LAMP_ACTIVE)
6687         {
6688           Bang(x, y + 1);
6689           return;
6690         }
6691         else if (smashed == EL_NUT)
6692         {
6693           Feld[x][y + 1] = EL_NUT_BREAKING;
6694           PlayLevelSound(x, y, SND_NUT_BREAKING);
6695           RaiseScoreElement(EL_NUT);
6696           return;
6697         }
6698         else if (smashed == EL_PEARL)
6699         {
6700           ResetGfxAnimation(x, y);
6701
6702           Feld[x][y + 1] = EL_PEARL_BREAKING;
6703           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6704           return;
6705         }
6706         else if (smashed == EL_DIAMOND)
6707         {
6708           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6709           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6710           return;
6711         }
6712         else if (IS_BELT_SWITCH(smashed))
6713         {
6714           ToggleBeltSwitch(x, y + 1);
6715         }
6716         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6717                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6718                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6719                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6720         {
6721           ToggleSwitchgateSwitch(x, y + 1);
6722         }
6723         else if (smashed == EL_LIGHT_SWITCH ||
6724                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6725         {
6726           ToggleLightSwitch(x, y + 1);
6727         }
6728         else
6729         {
6730 #if 0
6731           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6732 #endif
6733
6734           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6735
6736           CheckElementChangeBySide(x, y + 1, smashed, element,
6737                                    CE_SWITCHED, CH_SIDE_TOP);
6738           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6739                                             CH_SIDE_TOP);
6740         }
6741       }
6742       else
6743       {
6744         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6745       }
6746     }
6747   }
6748
6749   /* play sound of magic wall / mill */
6750   if (!last_line &&
6751       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6752        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6753        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6754   {
6755     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6756       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6757     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6758       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6759     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6760       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6761
6762     return;
6763   }
6764
6765   /* play sound of object that hits the ground */
6766   if (last_line || object_hit)
6767     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6768 }
6769
6770 inline static void TurnRoundExt(int x, int y)
6771 {
6772   static struct
6773   {
6774     int dx, dy;
6775   } move_xy[] =
6776   {
6777     {  0,  0 },
6778     { -1,  0 },
6779     { +1,  0 },
6780     {  0,  0 },
6781     {  0, -1 },
6782     {  0,  0 }, { 0, 0 }, { 0, 0 },
6783     {  0, +1 }
6784   };
6785   static struct
6786   {
6787     int left, right, back;
6788   } turn[] =
6789   {
6790     { 0,        0,              0        },
6791     { MV_DOWN,  MV_UP,          MV_RIGHT },
6792     { MV_UP,    MV_DOWN,        MV_LEFT  },
6793     { 0,        0,              0        },
6794     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6795     { 0,        0,              0        },
6796     { 0,        0,              0        },
6797     { 0,        0,              0        },
6798     { MV_RIGHT, MV_LEFT,        MV_UP    }
6799   };
6800
6801   int element = Feld[x][y];
6802   int move_pattern = element_info[element].move_pattern;
6803
6804   int old_move_dir = MovDir[x][y];
6805   int left_dir  = turn[old_move_dir].left;
6806   int right_dir = turn[old_move_dir].right;
6807   int back_dir  = turn[old_move_dir].back;
6808
6809   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6810   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6811   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6812   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6813
6814   int left_x  = x + left_dx,  left_y  = y + left_dy;
6815   int right_x = x + right_dx, right_y = y + right_dy;
6816   int move_x  = x + move_dx,  move_y  = y + move_dy;
6817
6818   int xx, yy;
6819
6820   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6821   {
6822     TestIfBadThingTouchesOtherBadThing(x, y);
6823
6824     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6825       MovDir[x][y] = right_dir;
6826     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6827       MovDir[x][y] = left_dir;
6828
6829     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6830       MovDelay[x][y] = 9;
6831     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6832       MovDelay[x][y] = 1;
6833   }
6834   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6835   {
6836     TestIfBadThingTouchesOtherBadThing(x, y);
6837
6838     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6839       MovDir[x][y] = left_dir;
6840     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6841       MovDir[x][y] = right_dir;
6842
6843     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6844       MovDelay[x][y] = 9;
6845     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6846       MovDelay[x][y] = 1;
6847   }
6848   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6849   {
6850     TestIfBadThingTouchesOtherBadThing(x, y);
6851
6852     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6853       MovDir[x][y] = left_dir;
6854     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6855       MovDir[x][y] = right_dir;
6856
6857     if (MovDir[x][y] != old_move_dir)
6858       MovDelay[x][y] = 9;
6859   }
6860   else if (element == EL_YAMYAM)
6861   {
6862     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6863     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6864
6865     if (can_turn_left && can_turn_right)
6866       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6867     else if (can_turn_left)
6868       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6869     else if (can_turn_right)
6870       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6871     else
6872       MovDir[x][y] = back_dir;
6873
6874     MovDelay[x][y] = 16 + 16 * RND(3);
6875   }
6876   else if (element == EL_DARK_YAMYAM)
6877   {
6878     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6879                                                          left_x, left_y);
6880     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6881                                                          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] = 16 + 16 * RND(3);
6893   }
6894   else if (element == EL_PACMAN)
6895   {
6896     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6897     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6898
6899     if (can_turn_left && can_turn_right)
6900       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6901     else if (can_turn_left)
6902       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6903     else if (can_turn_right)
6904       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6905     else
6906       MovDir[x][y] = back_dir;
6907
6908     MovDelay[x][y] = 6 + RND(40);
6909   }
6910   else if (element == EL_PIG)
6911   {
6912     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6913     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6914     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6915     boolean should_turn_left, should_turn_right, should_move_on;
6916     int rnd_value = 24;
6917     int rnd = RND(rnd_value);
6918
6919     should_turn_left = (can_turn_left &&
6920                         (!can_move_on ||
6921                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6922                                                    y + back_dy + left_dy)));
6923     should_turn_right = (can_turn_right &&
6924                          (!can_move_on ||
6925                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6926                                                     y + back_dy + right_dy)));
6927     should_move_on = (can_move_on &&
6928                       (!can_turn_left ||
6929                        !can_turn_right ||
6930                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6931                                                  y + move_dy + left_dy) ||
6932                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6933                                                  y + move_dy + right_dy)));
6934
6935     if (should_turn_left || should_turn_right || should_move_on)
6936     {
6937       if (should_turn_left && should_turn_right && should_move_on)
6938         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6939                         rnd < 2 * rnd_value / 3 ? right_dir :
6940                         old_move_dir);
6941       else if (should_turn_left && should_turn_right)
6942         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6943       else if (should_turn_left && should_move_on)
6944         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6945       else if (should_turn_right && should_move_on)
6946         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6947       else if (should_turn_left)
6948         MovDir[x][y] = left_dir;
6949       else if (should_turn_right)
6950         MovDir[x][y] = right_dir;
6951       else if (should_move_on)
6952         MovDir[x][y] = old_move_dir;
6953     }
6954     else if (can_move_on && rnd > rnd_value / 8)
6955       MovDir[x][y] = old_move_dir;
6956     else if (can_turn_left && can_turn_right)
6957       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6958     else if (can_turn_left && rnd > rnd_value / 8)
6959       MovDir[x][y] = left_dir;
6960     else if (can_turn_right && rnd > rnd_value/8)
6961       MovDir[x][y] = right_dir;
6962     else
6963       MovDir[x][y] = back_dir;
6964
6965     xx = x + move_xy[MovDir[x][y]].dx;
6966     yy = y + move_xy[MovDir[x][y]].dy;
6967
6968     if (!IN_LEV_FIELD(xx, yy) ||
6969         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6970       MovDir[x][y] = old_move_dir;
6971
6972     MovDelay[x][y] = 0;
6973   }
6974   else if (element == EL_DRAGON)
6975   {
6976     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6977     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6978     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6979     int rnd_value = 24;
6980     int rnd = RND(rnd_value);
6981
6982     if (can_move_on && rnd > rnd_value / 8)
6983       MovDir[x][y] = old_move_dir;
6984     else if (can_turn_left && can_turn_right)
6985       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6986     else if (can_turn_left && rnd > rnd_value / 8)
6987       MovDir[x][y] = left_dir;
6988     else if (can_turn_right && rnd > rnd_value / 8)
6989       MovDir[x][y] = right_dir;
6990     else
6991       MovDir[x][y] = back_dir;
6992
6993     xx = x + move_xy[MovDir[x][y]].dx;
6994     yy = y + move_xy[MovDir[x][y]].dy;
6995
6996     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6997       MovDir[x][y] = old_move_dir;
6998
6999     MovDelay[x][y] = 0;
7000   }
7001   else if (element == EL_MOLE)
7002   {
7003     boolean can_move_on =
7004       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7005                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7006                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7007     if (!can_move_on)
7008     {
7009       boolean can_turn_left =
7010         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7011                               IS_AMOEBOID(Feld[left_x][left_y])));
7012
7013       boolean can_turn_right =
7014         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7015                               IS_AMOEBOID(Feld[right_x][right_y])));
7016
7017       if (can_turn_left && can_turn_right)
7018         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7019       else if (can_turn_left)
7020         MovDir[x][y] = left_dir;
7021       else
7022         MovDir[x][y] = right_dir;
7023     }
7024
7025     if (MovDir[x][y] != old_move_dir)
7026       MovDelay[x][y] = 9;
7027   }
7028   else if (element == EL_BALLOON)
7029   {
7030     MovDir[x][y] = game.wind_direction;
7031     MovDelay[x][y] = 0;
7032   }
7033   else if (element == EL_SPRING)
7034   {
7035 #if USE_NEW_SPRING_BUMPER
7036     if (MovDir[x][y] & MV_HORIZONTAL)
7037     {
7038       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7039           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7040       {
7041         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7042         ResetGfxAnimation(move_x, move_y);
7043         TEST_DrawLevelField(move_x, move_y);
7044
7045         MovDir[x][y] = back_dir;
7046       }
7047       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7048                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7049         MovDir[x][y] = MV_NONE;
7050     }
7051 #else
7052     if (MovDir[x][y] & MV_HORIZONTAL &&
7053         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7054          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7055       MovDir[x][y] = MV_NONE;
7056 #endif
7057
7058     MovDelay[x][y] = 0;
7059   }
7060   else if (element == EL_ROBOT ||
7061            element == EL_SATELLITE ||
7062            element == EL_PENGUIN ||
7063            element == EL_EMC_ANDROID)
7064   {
7065     int attr_x = -1, attr_y = -1;
7066
7067     if (AllPlayersGone)
7068     {
7069       attr_x = ExitX;
7070       attr_y = ExitY;
7071     }
7072     else
7073     {
7074       int i;
7075
7076       for (i = 0; i < MAX_PLAYERS; i++)
7077       {
7078         struct PlayerInfo *player = &stored_player[i];
7079         int jx = player->jx, jy = player->jy;
7080
7081         if (!player->active)
7082           continue;
7083
7084         if (attr_x == -1 ||
7085             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7086         {
7087           attr_x = jx;
7088           attr_y = jy;
7089         }
7090       }
7091     }
7092
7093     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7094         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7095          game.engine_version < VERSION_IDENT(3,1,0,0)))
7096     {
7097       attr_x = ZX;
7098       attr_y = ZY;
7099     }
7100
7101     if (element == EL_PENGUIN)
7102     {
7103       int i;
7104       static int xy[4][2] =
7105       {
7106         { 0, -1 },
7107         { -1, 0 },
7108         { +1, 0 },
7109         { 0, +1 }
7110       };
7111
7112       for (i = 0; i < NUM_DIRECTIONS; i++)
7113       {
7114         int ex = x + xy[i][0];
7115         int ey = y + xy[i][1];
7116
7117         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7118                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7119                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7120                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7121         {
7122           attr_x = ex;
7123           attr_y = ey;
7124           break;
7125         }
7126       }
7127     }
7128
7129     MovDir[x][y] = MV_NONE;
7130     if (attr_x < x)
7131       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7132     else if (attr_x > x)
7133       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7134     if (attr_y < y)
7135       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7136     else if (attr_y > y)
7137       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7138
7139     if (element == EL_ROBOT)
7140     {
7141       int newx, newy;
7142
7143       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7144         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7145       Moving2Blocked(x, y, &newx, &newy);
7146
7147       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7148         MovDelay[x][y] = 8 + 8 * !RND(3);
7149       else
7150         MovDelay[x][y] = 16;
7151     }
7152     else if (element == EL_PENGUIN)
7153     {
7154       int newx, newy;
7155
7156       MovDelay[x][y] = 1;
7157
7158       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7159       {
7160         boolean first_horiz = RND(2);
7161         int new_move_dir = MovDir[x][y];
7162
7163         MovDir[x][y] =
7164           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7165         Moving2Blocked(x, y, &newx, &newy);
7166
7167         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7168           return;
7169
7170         MovDir[x][y] =
7171           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7172         Moving2Blocked(x, y, &newx, &newy);
7173
7174         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7175           return;
7176
7177         MovDir[x][y] = old_move_dir;
7178         return;
7179       }
7180     }
7181     else if (element == EL_SATELLITE)
7182     {
7183       int newx, newy;
7184
7185       MovDelay[x][y] = 1;
7186
7187       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7188       {
7189         boolean first_horiz = RND(2);
7190         int new_move_dir = MovDir[x][y];
7191
7192         MovDir[x][y] =
7193           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7194         Moving2Blocked(x, y, &newx, &newy);
7195
7196         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7197           return;
7198
7199         MovDir[x][y] =
7200           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7201         Moving2Blocked(x, y, &newx, &newy);
7202
7203         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7204           return;
7205
7206         MovDir[x][y] = old_move_dir;
7207         return;
7208       }
7209     }
7210     else if (element == EL_EMC_ANDROID)
7211     {
7212       static int check_pos[16] =
7213       {
7214         -1,             /*  0 => (invalid)          */
7215         7,              /*  1 => MV_LEFT            */
7216         3,              /*  2 => MV_RIGHT           */
7217         -1,             /*  3 => (invalid)          */
7218         1,              /*  4 =>            MV_UP   */
7219         0,              /*  5 => MV_LEFT  | MV_UP   */
7220         2,              /*  6 => MV_RIGHT | MV_UP   */
7221         -1,             /*  7 => (invalid)          */
7222         5,              /*  8 =>            MV_DOWN */
7223         6,              /*  9 => MV_LEFT  | MV_DOWN */
7224         4,              /* 10 => MV_RIGHT | MV_DOWN */
7225         -1,             /* 11 => (invalid)          */
7226         -1,             /* 12 => (invalid)          */
7227         -1,             /* 13 => (invalid)          */
7228         -1,             /* 14 => (invalid)          */
7229         -1,             /* 15 => (invalid)          */
7230       };
7231       static struct
7232       {
7233         int dx, dy;
7234         int dir;
7235       } check_xy[8] =
7236       {
7237         { -1, -1,       MV_LEFT  | MV_UP   },
7238         {  0, -1,                  MV_UP   },
7239         { +1, -1,       MV_RIGHT | MV_UP   },
7240         { +1,  0,       MV_RIGHT           },
7241         { +1, +1,       MV_RIGHT | MV_DOWN },
7242         {  0, +1,                  MV_DOWN },
7243         { -1, +1,       MV_LEFT  | MV_DOWN },
7244         { -1,  0,       MV_LEFT            },
7245       };
7246       int start_pos, check_order;
7247       boolean can_clone = FALSE;
7248       int i;
7249
7250       /* check if there is any free field around current position */
7251       for (i = 0; i < 8; i++)
7252       {
7253         int newx = x + check_xy[i].dx;
7254         int newy = y + check_xy[i].dy;
7255
7256         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7257         {
7258           can_clone = TRUE;
7259
7260           break;
7261         }
7262       }
7263
7264       if (can_clone)            /* randomly find an element to clone */
7265       {
7266         can_clone = FALSE;
7267
7268         start_pos = check_pos[RND(8)];
7269         check_order = (RND(2) ? -1 : +1);
7270
7271         for (i = 0; i < 8; i++)
7272         {
7273           int pos_raw = start_pos + i * check_order;
7274           int pos = (pos_raw + 8) % 8;
7275           int newx = x + check_xy[pos].dx;
7276           int newy = y + check_xy[pos].dy;
7277
7278           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7279           {
7280             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7281             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7282
7283             Store[x][y] = Feld[newx][newy];
7284
7285             can_clone = TRUE;
7286
7287             break;
7288           }
7289         }
7290       }
7291
7292       if (can_clone)            /* randomly find a direction to move */
7293       {
7294         can_clone = FALSE;
7295
7296         start_pos = check_pos[RND(8)];
7297         check_order = (RND(2) ? -1 : +1);
7298
7299         for (i = 0; i < 8; i++)
7300         {
7301           int pos_raw = start_pos + i * check_order;
7302           int pos = (pos_raw + 8) % 8;
7303           int newx = x + check_xy[pos].dx;
7304           int newy = y + check_xy[pos].dy;
7305           int new_move_dir = check_xy[pos].dir;
7306
7307           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7308           {
7309             MovDir[x][y] = new_move_dir;
7310             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7311
7312             can_clone = TRUE;
7313
7314             break;
7315           }
7316         }
7317       }
7318
7319       if (can_clone)            /* cloning and moving successful */
7320         return;
7321
7322       /* cannot clone -- try to move towards player */
7323
7324       start_pos = check_pos[MovDir[x][y] & 0x0f];
7325       check_order = (RND(2) ? -1 : +1);
7326
7327       for (i = 0; i < 3; i++)
7328       {
7329         /* first check start_pos, then previous/next or (next/previous) pos */
7330         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7331         int pos = (pos_raw + 8) % 8;
7332         int newx = x + check_xy[pos].dx;
7333         int newy = y + check_xy[pos].dy;
7334         int new_move_dir = check_xy[pos].dir;
7335
7336         if (IS_PLAYER(newx, newy))
7337           break;
7338
7339         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7340         {
7341           MovDir[x][y] = new_move_dir;
7342           MovDelay[x][y] = level.android_move_time * 8 + 1;
7343
7344           break;
7345         }
7346       }
7347     }
7348   }
7349   else if (move_pattern == MV_TURNING_LEFT ||
7350            move_pattern == MV_TURNING_RIGHT ||
7351            move_pattern == MV_TURNING_LEFT_RIGHT ||
7352            move_pattern == MV_TURNING_RIGHT_LEFT ||
7353            move_pattern == MV_TURNING_RANDOM ||
7354            move_pattern == MV_ALL_DIRECTIONS)
7355   {
7356     boolean can_turn_left =
7357       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7358     boolean can_turn_right =
7359       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7360
7361     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7362       return;
7363
7364     if (move_pattern == MV_TURNING_LEFT)
7365       MovDir[x][y] = left_dir;
7366     else if (move_pattern == MV_TURNING_RIGHT)
7367       MovDir[x][y] = right_dir;
7368     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7369       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7370     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7371       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7372     else if (move_pattern == MV_TURNING_RANDOM)
7373       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7374                       can_turn_right && !can_turn_left ? right_dir :
7375                       RND(2) ? left_dir : right_dir);
7376     else if (can_turn_left && can_turn_right)
7377       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7378     else if (can_turn_left)
7379       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7380     else if (can_turn_right)
7381       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7382     else
7383       MovDir[x][y] = back_dir;
7384
7385     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7386   }
7387   else if (move_pattern == MV_HORIZONTAL ||
7388            move_pattern == MV_VERTICAL)
7389   {
7390     if (move_pattern & old_move_dir)
7391       MovDir[x][y] = back_dir;
7392     else if (move_pattern == MV_HORIZONTAL)
7393       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7394     else if (move_pattern == MV_VERTICAL)
7395       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7396
7397     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7398   }
7399   else if (move_pattern & MV_ANY_DIRECTION)
7400   {
7401     MovDir[x][y] = move_pattern;
7402     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7403   }
7404   else if (move_pattern & MV_WIND_DIRECTION)
7405   {
7406     MovDir[x][y] = game.wind_direction;
7407     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7408   }
7409   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7410   {
7411     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7412       MovDir[x][y] = left_dir;
7413     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7414       MovDir[x][y] = right_dir;
7415
7416     if (MovDir[x][y] != old_move_dir)
7417       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7418   }
7419   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7420   {
7421     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7422       MovDir[x][y] = right_dir;
7423     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7424       MovDir[x][y] = left_dir;
7425
7426     if (MovDir[x][y] != old_move_dir)
7427       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7428   }
7429   else if (move_pattern == MV_TOWARDS_PLAYER ||
7430            move_pattern == MV_AWAY_FROM_PLAYER)
7431   {
7432     int attr_x = -1, attr_y = -1;
7433     int newx, newy;
7434     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7435
7436     if (AllPlayersGone)
7437     {
7438       attr_x = ExitX;
7439       attr_y = ExitY;
7440     }
7441     else
7442     {
7443       int i;
7444
7445       for (i = 0; i < MAX_PLAYERS; i++)
7446       {
7447         struct PlayerInfo *player = &stored_player[i];
7448         int jx = player->jx, jy = player->jy;
7449
7450         if (!player->active)
7451           continue;
7452
7453         if (attr_x == -1 ||
7454             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7455         {
7456           attr_x = jx;
7457           attr_y = jy;
7458         }
7459       }
7460     }
7461
7462     MovDir[x][y] = MV_NONE;
7463     if (attr_x < x)
7464       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7465     else if (attr_x > x)
7466       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7467     if (attr_y < y)
7468       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7469     else if (attr_y > y)
7470       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7471
7472     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7473
7474     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7475     {
7476       boolean first_horiz = RND(2);
7477       int new_move_dir = MovDir[x][y];
7478
7479       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7480       {
7481         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7482         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7483
7484         return;
7485       }
7486
7487       MovDir[x][y] =
7488         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489       Moving2Blocked(x, y, &newx, &newy);
7490
7491       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7492         return;
7493
7494       MovDir[x][y] =
7495         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496       Moving2Blocked(x, y, &newx, &newy);
7497
7498       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7499         return;
7500
7501       MovDir[x][y] = old_move_dir;
7502     }
7503   }
7504   else if (move_pattern == MV_WHEN_PUSHED ||
7505            move_pattern == MV_WHEN_DROPPED)
7506   {
7507     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7508       MovDir[x][y] = MV_NONE;
7509
7510     MovDelay[x][y] = 0;
7511   }
7512   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7513   {
7514     static int test_xy[7][2] =
7515     {
7516       { 0, -1 },
7517       { -1, 0 },
7518       { +1, 0 },
7519       { 0, +1 },
7520       { 0, -1 },
7521       { -1, 0 },
7522       { +1, 0 },
7523     };
7524     static int test_dir[7] =
7525     {
7526       MV_UP,
7527       MV_LEFT,
7528       MV_RIGHT,
7529       MV_DOWN,
7530       MV_UP,
7531       MV_LEFT,
7532       MV_RIGHT,
7533     };
7534     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7535     int move_preference = -1000000;     /* start with very low preference */
7536     int new_move_dir = MV_NONE;
7537     int start_test = RND(4);
7538     int i;
7539
7540     for (i = 0; i < NUM_DIRECTIONS; i++)
7541     {
7542       int move_dir = test_dir[start_test + i];
7543       int move_dir_preference;
7544
7545       xx = x + test_xy[start_test + i][0];
7546       yy = y + test_xy[start_test + i][1];
7547
7548       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7549           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7550       {
7551         new_move_dir = move_dir;
7552
7553         break;
7554       }
7555
7556       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7557         continue;
7558
7559       move_dir_preference = -1 * RunnerVisit[xx][yy];
7560       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7561         move_dir_preference = PlayerVisit[xx][yy];
7562
7563       if (move_dir_preference > move_preference)
7564       {
7565         /* prefer field that has not been visited for the longest time */
7566         move_preference = move_dir_preference;
7567         new_move_dir = move_dir;
7568       }
7569       else if (move_dir_preference == move_preference &&
7570                move_dir == old_move_dir)
7571       {
7572         /* prefer last direction when all directions are preferred equally */
7573         move_preference = move_dir_preference;
7574         new_move_dir = move_dir;
7575       }
7576     }
7577
7578     MovDir[x][y] = new_move_dir;
7579     if (old_move_dir != new_move_dir)
7580       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7581   }
7582 }
7583
7584 static void TurnRound(int x, int y)
7585 {
7586   int direction = MovDir[x][y];
7587
7588   TurnRoundExt(x, y);
7589
7590   GfxDir[x][y] = MovDir[x][y];
7591
7592   if (direction != MovDir[x][y])
7593     GfxFrame[x][y] = 0;
7594
7595   if (MovDelay[x][y])
7596     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7597
7598   ResetGfxFrame(x, y, FALSE);
7599 }
7600
7601 static boolean JustBeingPushed(int x, int y)
7602 {
7603   int i;
7604
7605   for (i = 0; i < MAX_PLAYERS; i++)
7606   {
7607     struct PlayerInfo *player = &stored_player[i];
7608
7609     if (player->active && player->is_pushing && player->MovPos)
7610     {
7611       int next_jx = player->jx + (player->jx - player->last_jx);
7612       int next_jy = player->jy + (player->jy - player->last_jy);
7613
7614       if (x == next_jx && y == next_jy)
7615         return TRUE;
7616     }
7617   }
7618
7619   return FALSE;
7620 }
7621
7622 void StartMoving(int x, int y)
7623 {
7624   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7625   int element = Feld[x][y];
7626
7627   if (Stop[x][y])
7628     return;
7629
7630   if (MovDelay[x][y] == 0)
7631     GfxAction[x][y] = ACTION_DEFAULT;
7632
7633   if (CAN_FALL(element) && y < lev_fieldy - 1)
7634   {
7635     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7636         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7637       if (JustBeingPushed(x, y))
7638         return;
7639
7640     if (element == EL_QUICKSAND_FULL)
7641     {
7642       if (IS_FREE(x, y + 1))
7643       {
7644         InitMovingField(x, y, MV_DOWN);
7645         started_moving = TRUE;
7646
7647         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7648 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7649         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7650           Store[x][y] = EL_ROCK;
7651 #else
7652         Store[x][y] = EL_ROCK;
7653 #endif
7654
7655         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7656       }
7657       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7658       {
7659         if (!MovDelay[x][y])
7660         {
7661           MovDelay[x][y] = TILEY + 1;
7662
7663           ResetGfxAnimation(x, y);
7664           ResetGfxAnimation(x, y + 1);
7665         }
7666
7667         if (MovDelay[x][y])
7668         {
7669           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7670           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7671
7672           MovDelay[x][y]--;
7673           if (MovDelay[x][y])
7674             return;
7675         }
7676
7677         Feld[x][y] = EL_QUICKSAND_EMPTY;
7678         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7679         Store[x][y + 1] = Store[x][y];
7680         Store[x][y] = 0;
7681
7682         PlayLevelSoundAction(x, y, ACTION_FILLING);
7683       }
7684       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7685       {
7686         if (!MovDelay[x][y])
7687         {
7688           MovDelay[x][y] = TILEY + 1;
7689
7690           ResetGfxAnimation(x, y);
7691           ResetGfxAnimation(x, y + 1);
7692         }
7693
7694         if (MovDelay[x][y])
7695         {
7696           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7697           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7698
7699           MovDelay[x][y]--;
7700           if (MovDelay[x][y])
7701             return;
7702         }
7703
7704         Feld[x][y] = EL_QUICKSAND_EMPTY;
7705         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7706         Store[x][y + 1] = Store[x][y];
7707         Store[x][y] = 0;
7708
7709         PlayLevelSoundAction(x, y, ACTION_FILLING);
7710       }
7711     }
7712     else if (element == EL_QUICKSAND_FAST_FULL)
7713     {
7714       if (IS_FREE(x, y + 1))
7715       {
7716         InitMovingField(x, y, MV_DOWN);
7717         started_moving = TRUE;
7718
7719         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7720 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7721         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7722           Store[x][y] = EL_ROCK;
7723 #else
7724         Store[x][y] = EL_ROCK;
7725 #endif
7726
7727         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7728       }
7729       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7730       {
7731         if (!MovDelay[x][y])
7732         {
7733           MovDelay[x][y] = TILEY + 1;
7734
7735           ResetGfxAnimation(x, y);
7736           ResetGfxAnimation(x, y + 1);
7737         }
7738
7739         if (MovDelay[x][y])
7740         {
7741           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7742           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7743
7744           MovDelay[x][y]--;
7745           if (MovDelay[x][y])
7746             return;
7747         }
7748
7749         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7750         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7751         Store[x][y + 1] = Store[x][y];
7752         Store[x][y] = 0;
7753
7754         PlayLevelSoundAction(x, y, ACTION_FILLING);
7755       }
7756       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7757       {
7758         if (!MovDelay[x][y])
7759         {
7760           MovDelay[x][y] = TILEY + 1;
7761
7762           ResetGfxAnimation(x, y);
7763           ResetGfxAnimation(x, y + 1);
7764         }
7765
7766         if (MovDelay[x][y])
7767         {
7768           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7769           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7770
7771           MovDelay[x][y]--;
7772           if (MovDelay[x][y])
7773             return;
7774         }
7775
7776         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7777         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7778         Store[x][y + 1] = Store[x][y];
7779         Store[x][y] = 0;
7780
7781         PlayLevelSoundAction(x, y, ACTION_FILLING);
7782       }
7783     }
7784     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7785              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7786     {
7787       InitMovingField(x, y, MV_DOWN);
7788       started_moving = TRUE;
7789
7790       Feld[x][y] = EL_QUICKSAND_FILLING;
7791       Store[x][y] = element;
7792
7793       PlayLevelSoundAction(x, y, ACTION_FILLING);
7794     }
7795     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7796              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7797     {
7798       InitMovingField(x, y, MV_DOWN);
7799       started_moving = TRUE;
7800
7801       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7802       Store[x][y] = element;
7803
7804       PlayLevelSoundAction(x, y, ACTION_FILLING);
7805     }
7806     else if (element == EL_MAGIC_WALL_FULL)
7807     {
7808       if (IS_FREE(x, y + 1))
7809       {
7810         InitMovingField(x, y, MV_DOWN);
7811         started_moving = TRUE;
7812
7813         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7814         Store[x][y] = EL_CHANGED(Store[x][y]);
7815       }
7816       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7817       {
7818         if (!MovDelay[x][y])
7819           MovDelay[x][y] = TILEY/4 + 1;
7820
7821         if (MovDelay[x][y])
7822         {
7823           MovDelay[x][y]--;
7824           if (MovDelay[x][y])
7825             return;
7826         }
7827
7828         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7829         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7830         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7831         Store[x][y] = 0;
7832       }
7833     }
7834     else if (element == EL_BD_MAGIC_WALL_FULL)
7835     {
7836       if (IS_FREE(x, y + 1))
7837       {
7838         InitMovingField(x, y, MV_DOWN);
7839         started_moving = TRUE;
7840
7841         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7842         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7843       }
7844       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7845       {
7846         if (!MovDelay[x][y])
7847           MovDelay[x][y] = TILEY/4 + 1;
7848
7849         if (MovDelay[x][y])
7850         {
7851           MovDelay[x][y]--;
7852           if (MovDelay[x][y])
7853             return;
7854         }
7855
7856         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7857         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7858         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7859         Store[x][y] = 0;
7860       }
7861     }
7862     else if (element == EL_DC_MAGIC_WALL_FULL)
7863     {
7864       if (IS_FREE(x, y + 1))
7865       {
7866         InitMovingField(x, y, MV_DOWN);
7867         started_moving = TRUE;
7868
7869         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7870         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7871       }
7872       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7873       {
7874         if (!MovDelay[x][y])
7875           MovDelay[x][y] = TILEY/4 + 1;
7876
7877         if (MovDelay[x][y])
7878         {
7879           MovDelay[x][y]--;
7880           if (MovDelay[x][y])
7881             return;
7882         }
7883
7884         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7885         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7886         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7887         Store[x][y] = 0;
7888       }
7889     }
7890     else if ((CAN_PASS_MAGIC_WALL(element) &&
7891               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7892                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7893              (CAN_PASS_DC_MAGIC_WALL(element) &&
7894               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7895
7896     {
7897       InitMovingField(x, y, MV_DOWN);
7898       started_moving = TRUE;
7899
7900       Feld[x][y] =
7901         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7902          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7903          EL_DC_MAGIC_WALL_FILLING);
7904       Store[x][y] = element;
7905     }
7906     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7907     {
7908       SplashAcid(x, y + 1);
7909
7910       InitMovingField(x, y, MV_DOWN);
7911       started_moving = TRUE;
7912
7913       Store[x][y] = EL_ACID;
7914     }
7915     else if (
7916 #if USE_FIX_IMPACT_COLLISION
7917              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7918               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7919 #else
7920              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7921               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7922 #endif
7923              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7924               CAN_FALL(element) && WasJustFalling[x][y] &&
7925               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7926
7927              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7928               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7929               (Feld[x][y + 1] == EL_BLOCKED)))
7930     {
7931       /* this is needed for a special case not covered by calling "Impact()"
7932          from "ContinueMoving()": if an element moves to a tile directly below
7933          another element which was just falling on that tile (which was empty
7934          in the previous frame), the falling element above would just stop
7935          instead of smashing the element below (in previous version, the above
7936          element was just checked for "moving" instead of "falling", resulting
7937          in incorrect smashes caused by horizontal movement of the above
7938          element; also, the case of the player being the element to smash was
7939          simply not covered here... :-/ ) */
7940
7941       CheckCollision[x][y] = 0;
7942       CheckImpact[x][y] = 0;
7943
7944       Impact(x, y);
7945     }
7946     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7947     {
7948       if (MovDir[x][y] == MV_NONE)
7949       {
7950         InitMovingField(x, y, MV_DOWN);
7951         started_moving = TRUE;
7952       }
7953     }
7954     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7955     {
7956       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7957         MovDir[x][y] = MV_DOWN;
7958
7959       InitMovingField(x, y, MV_DOWN);
7960       started_moving = TRUE;
7961     }
7962     else if (element == EL_AMOEBA_DROP)
7963     {
7964       Feld[x][y] = EL_AMOEBA_GROWING;
7965       Store[x][y] = EL_AMOEBA_WET;
7966     }
7967     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7968               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7969              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7970              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7971     {
7972       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7973                                 (IS_FREE(x - 1, y + 1) ||
7974                                  Feld[x - 1][y + 1] == EL_ACID));
7975       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7976                                 (IS_FREE(x + 1, y + 1) ||
7977                                  Feld[x + 1][y + 1] == EL_ACID));
7978       boolean can_fall_any  = (can_fall_left || can_fall_right);
7979       boolean can_fall_both = (can_fall_left && can_fall_right);
7980       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7981
7982 #if USE_NEW_ALL_SLIPPERY
7983       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7984       {
7985         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7988           can_fall_left = FALSE;
7989         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7990           can_fall_right = FALSE;
7991         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7992           can_fall_left = FALSE;
7993
7994         can_fall_any  = (can_fall_left || can_fall_right);
7995         can_fall_both = FALSE;
7996       }
7997 #else
7998       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7999       {
8000         if (slippery_type == SLIPPERY_ONLY_LEFT)
8001           can_fall_right = FALSE;
8002         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8003           can_fall_left = FALSE;
8004         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8005           can_fall_right = FALSE;
8006         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8007           can_fall_left = FALSE;
8008
8009         can_fall_any  = (can_fall_left || can_fall_right);
8010         can_fall_both = (can_fall_left && can_fall_right);
8011       }
8012 #endif
8013
8014 #if USE_NEW_ALL_SLIPPERY
8015 #else
8016 #if USE_NEW_SP_SLIPPERY
8017       /* !!! better use the same properties as for custom elements here !!! */
8018       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8019                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8020       {
8021         can_fall_right = FALSE;         /* slip down on left side */
8022         can_fall_both = FALSE;
8023       }
8024 #endif
8025 #endif
8026
8027 #if USE_NEW_ALL_SLIPPERY
8028       if (can_fall_both)
8029       {
8030         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8031           can_fall_right = FALSE;       /* slip down on left side */
8032         else
8033           can_fall_left = !(can_fall_right = RND(2));
8034
8035         can_fall_both = FALSE;
8036       }
8037 #else
8038       if (can_fall_both)
8039       {
8040         if (game.emulation == EMU_BOULDERDASH ||
8041             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8042           can_fall_right = FALSE;       /* slip down on left side */
8043         else
8044           can_fall_left = !(can_fall_right = RND(2));
8045
8046         can_fall_both = FALSE;
8047       }
8048 #endif
8049
8050       if (can_fall_any)
8051       {
8052         /* if not determined otherwise, prefer left side for slipping down */
8053         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8054         started_moving = TRUE;
8055       }
8056     }
8057 #if 0
8058     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8059 #else
8060     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8061 #endif
8062     {
8063       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8064       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8065       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8066       int belt_dir = game.belt_dir[belt_nr];
8067
8068       if ((belt_dir == MV_LEFT  && left_is_free) ||
8069           (belt_dir == MV_RIGHT && right_is_free))
8070       {
8071         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8072
8073         InitMovingField(x, y, belt_dir);
8074         started_moving = TRUE;
8075
8076         Pushed[x][y] = TRUE;
8077         Pushed[nextx][y] = TRUE;
8078
8079         GfxAction[x][y] = ACTION_DEFAULT;
8080       }
8081       else
8082       {
8083         MovDir[x][y] = 0;       /* if element was moving, stop it */
8084       }
8085     }
8086   }
8087
8088   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8089 #if 0
8090   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8091 #else
8092   if (CAN_MOVE(element) && !started_moving)
8093 #endif
8094   {
8095     int move_pattern = element_info[element].move_pattern;
8096     int newx, newy;
8097
8098 #if 0
8099 #if DEBUG
8100     if (MovDir[x][y] == MV_NONE)
8101     {
8102       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8103              x, y, element, element_info[element].token_name);
8104       printf("StartMoving(): This should never happen!\n");
8105     }
8106 #endif
8107 #endif
8108
8109     Moving2Blocked(x, y, &newx, &newy);
8110
8111     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8112       return;
8113
8114     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8115         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8116     {
8117       WasJustMoving[x][y] = 0;
8118       CheckCollision[x][y] = 0;
8119
8120       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8121
8122       if (Feld[x][y] != element)        /* element has changed */
8123         return;
8124     }
8125
8126     if (!MovDelay[x][y])        /* start new movement phase */
8127     {
8128       /* all objects that can change their move direction after each step
8129          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8130
8131       if (element != EL_YAMYAM &&
8132           element != EL_DARK_YAMYAM &&
8133           element != EL_PACMAN &&
8134           !(move_pattern & MV_ANY_DIRECTION) &&
8135           move_pattern != MV_TURNING_LEFT &&
8136           move_pattern != MV_TURNING_RIGHT &&
8137           move_pattern != MV_TURNING_LEFT_RIGHT &&
8138           move_pattern != MV_TURNING_RIGHT_LEFT &&
8139           move_pattern != MV_TURNING_RANDOM)
8140       {
8141         TurnRound(x, y);
8142
8143         if (MovDelay[x][y] && (element == EL_BUG ||
8144                                element == EL_SPACESHIP ||
8145                                element == EL_SP_SNIKSNAK ||
8146                                element == EL_SP_ELECTRON ||
8147                                element == EL_MOLE))
8148           TEST_DrawLevelField(x, y);
8149       }
8150     }
8151
8152     if (MovDelay[x][y])         /* wait some time before next movement */
8153     {
8154       MovDelay[x][y]--;
8155
8156       if (element == EL_ROBOT ||
8157           element == EL_YAMYAM ||
8158           element == EL_DARK_YAMYAM)
8159       {
8160         DrawLevelElementAnimationIfNeeded(x, y, element);
8161         PlayLevelSoundAction(x, y, ACTION_WAITING);
8162       }
8163       else if (element == EL_SP_ELECTRON)
8164         DrawLevelElementAnimationIfNeeded(x, y, element);
8165       else if (element == EL_DRAGON)
8166       {
8167         int i;
8168         int dir = MovDir[x][y];
8169         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8170         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8171         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8172                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8173                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8174                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8175         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8176
8177         GfxAction[x][y] = ACTION_ATTACKING;
8178
8179         if (IS_PLAYER(x, y))
8180           DrawPlayerField(x, y);
8181         else
8182           TEST_DrawLevelField(x, y);
8183
8184         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8185
8186         for (i = 1; i <= 3; i++)
8187         {
8188           int xx = x + i * dx;
8189           int yy = y + i * dy;
8190           int sx = SCREENX(xx);
8191           int sy = SCREENY(yy);
8192           int flame_graphic = graphic + (i - 1);
8193
8194           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8195             break;
8196
8197           if (MovDelay[x][y])
8198           {
8199             int flamed = MovingOrBlocked2Element(xx, yy);
8200
8201             /* !!! */
8202 #if 0
8203             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8204               Bang(xx, yy);
8205             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8206               RemoveMovingField(xx, yy);
8207             else
8208               RemoveField(xx, yy);
8209 #else
8210             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8211               Bang(xx, yy);
8212             else
8213               RemoveMovingField(xx, yy);
8214 #endif
8215
8216             ChangeDelay[xx][yy] = 0;
8217
8218             Feld[xx][yy] = EL_FLAMES;
8219
8220             if (IN_SCR_FIELD(sx, sy))
8221             {
8222               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8223               DrawGraphic(sx, sy, flame_graphic, frame);
8224             }
8225           }
8226           else
8227           {
8228             if (Feld[xx][yy] == EL_FLAMES)
8229               Feld[xx][yy] = EL_EMPTY;
8230             TEST_DrawLevelField(xx, yy);
8231           }
8232         }
8233       }
8234
8235       if (MovDelay[x][y])       /* element still has to wait some time */
8236       {
8237         PlayLevelSoundAction(x, y, ACTION_WAITING);
8238
8239         return;
8240       }
8241     }
8242
8243     /* now make next step */
8244
8245     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8246
8247     if (DONT_COLLIDE_WITH(element) &&
8248         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8249         !PLAYER_ENEMY_PROTECTED(newx, newy))
8250     {
8251       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8252
8253       return;
8254     }
8255
8256     else if (CAN_MOVE_INTO_ACID(element) &&
8257              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8258              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8259              (MovDir[x][y] == MV_DOWN ||
8260               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8261     {
8262       SplashAcid(newx, newy);
8263       Store[x][y] = EL_ACID;
8264     }
8265     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8266     {
8267       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8268           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8269           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8270           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8271       {
8272         RemoveField(x, y);
8273         TEST_DrawLevelField(x, y);
8274
8275         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8276         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8277           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8278
8279         local_player->friends_still_needed--;
8280         if (!local_player->friends_still_needed &&
8281             !local_player->GameOver && AllPlayersGone)
8282           PlayerWins(local_player);
8283
8284         return;
8285       }
8286       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8287       {
8288         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8289           TEST_DrawLevelField(newx, newy);
8290         else
8291           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8292       }
8293       else if (!IS_FREE(newx, newy))
8294       {
8295         GfxAction[x][y] = ACTION_WAITING;
8296
8297         if (IS_PLAYER(x, y))
8298           DrawPlayerField(x, y);
8299         else
8300           TEST_DrawLevelField(x, y);
8301
8302         return;
8303       }
8304     }
8305     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8306     {
8307       if (IS_FOOD_PIG(Feld[newx][newy]))
8308       {
8309         if (IS_MOVING(newx, newy))
8310           RemoveMovingField(newx, newy);
8311         else
8312         {
8313           Feld[newx][newy] = EL_EMPTY;
8314           TEST_DrawLevelField(newx, newy);
8315         }
8316
8317         PlayLevelSound(x, y, SND_PIG_DIGGING);
8318       }
8319       else if (!IS_FREE(newx, newy))
8320       {
8321         if (IS_PLAYER(x, y))
8322           DrawPlayerField(x, y);
8323         else
8324           TEST_DrawLevelField(x, y);
8325
8326         return;
8327       }
8328     }
8329     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8330     {
8331       if (Store[x][y] != EL_EMPTY)
8332       {
8333         boolean can_clone = FALSE;
8334         int xx, yy;
8335
8336         /* check if element to clone is still there */
8337         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8338         {
8339           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8340           {
8341             can_clone = TRUE;
8342
8343             break;
8344           }
8345         }
8346
8347         /* cannot clone or target field not free anymore -- do not clone */
8348         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8349           Store[x][y] = EL_EMPTY;
8350       }
8351
8352       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8353       {
8354         if (IS_MV_DIAGONAL(MovDir[x][y]))
8355         {
8356           int diagonal_move_dir = MovDir[x][y];
8357           int stored = Store[x][y];
8358           int change_delay = 8;
8359           int graphic;
8360
8361           /* android is moving diagonally */
8362
8363           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8364
8365           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8366           GfxElement[x][y] = EL_EMC_ANDROID;
8367           GfxAction[x][y] = ACTION_SHRINKING;
8368           GfxDir[x][y] = diagonal_move_dir;
8369           ChangeDelay[x][y] = change_delay;
8370
8371           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8372                                    GfxDir[x][y]);
8373
8374           DrawLevelGraphicAnimation(x, y, graphic);
8375           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8376
8377           if (Feld[newx][newy] == EL_ACID)
8378           {
8379             SplashAcid(newx, newy);
8380
8381             return;
8382           }
8383
8384           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8385
8386           Store[newx][newy] = EL_EMC_ANDROID;
8387           GfxElement[newx][newy] = EL_EMC_ANDROID;
8388           GfxAction[newx][newy] = ACTION_GROWING;
8389           GfxDir[newx][newy] = diagonal_move_dir;
8390           ChangeDelay[newx][newy] = change_delay;
8391
8392           graphic = el_act_dir2img(GfxElement[newx][newy],
8393                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8394
8395           DrawLevelGraphicAnimation(newx, newy, graphic);
8396           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8397
8398           return;
8399         }
8400         else
8401         {
8402           Feld[newx][newy] = EL_EMPTY;
8403           TEST_DrawLevelField(newx, newy);
8404
8405           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8406         }
8407       }
8408       else if (!IS_FREE(newx, newy))
8409       {
8410 #if 0
8411         if (IS_PLAYER(x, y))
8412           DrawPlayerField(x, y);
8413         else
8414           TEST_DrawLevelField(x, y);
8415 #endif
8416
8417         return;
8418       }
8419     }
8420     else if (IS_CUSTOM_ELEMENT(element) &&
8421              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8422     {
8423 #if 1
8424       if (!DigFieldByCE(newx, newy, element))
8425         return;
8426 #else
8427       int new_element = Feld[newx][newy];
8428
8429       if (!IS_FREE(newx, newy))
8430       {
8431         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8432                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8433                       ACTION_BREAKING);
8434
8435         /* no element can dig solid indestructible elements */
8436         if (IS_INDESTRUCTIBLE(new_element) &&
8437             !IS_DIGGABLE(new_element) &&
8438             !IS_COLLECTIBLE(new_element))
8439           return;
8440
8441         if (AmoebaNr[newx][newy] &&
8442             (new_element == EL_AMOEBA_FULL ||
8443              new_element == EL_BD_AMOEBA ||
8444              new_element == EL_AMOEBA_GROWING))
8445         {
8446           AmoebaCnt[AmoebaNr[newx][newy]]--;
8447           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8448         }
8449
8450         if (IS_MOVING(newx, newy))
8451           RemoveMovingField(newx, newy);
8452         else
8453         {
8454           RemoveField(newx, newy);
8455           TEST_DrawLevelField(newx, newy);
8456         }
8457
8458         /* if digged element was about to explode, prevent the explosion */
8459         ExplodeField[newx][newy] = EX_TYPE_NONE;
8460
8461         PlayLevelSoundAction(x, y, action);
8462       }
8463
8464       Store[newx][newy] = EL_EMPTY;
8465
8466 #if 1
8467       /* this makes it possible to leave the removed element again */
8468       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8469         Store[newx][newy] = new_element;
8470 #else
8471       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8472       {
8473         int move_leave_element = element_info[element].move_leave_element;
8474
8475         /* this makes it possible to leave the removed element again */
8476         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8477                              new_element : move_leave_element);
8478       }
8479 #endif
8480
8481 #endif
8482
8483       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8484       {
8485         RunnerVisit[x][y] = FrameCounter;
8486         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8487       }
8488     }
8489     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8490     {
8491       if (!IS_FREE(newx, newy))
8492       {
8493         if (IS_PLAYER(x, y))
8494           DrawPlayerField(x, y);
8495         else
8496           TEST_DrawLevelField(x, y);
8497
8498         return;
8499       }
8500       else
8501       {
8502         boolean wanna_flame = !RND(10);
8503         int dx = newx - x, dy = newy - y;
8504         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8505         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8506         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8507                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8508         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8509                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8510
8511         if ((wanna_flame ||
8512              IS_CLASSIC_ENEMY(element1) ||
8513              IS_CLASSIC_ENEMY(element2)) &&
8514             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8515             element1 != EL_FLAMES && element2 != EL_FLAMES)
8516         {
8517           ResetGfxAnimation(x, y);
8518           GfxAction[x][y] = ACTION_ATTACKING;
8519
8520           if (IS_PLAYER(x, y))
8521             DrawPlayerField(x, y);
8522           else
8523             TEST_DrawLevelField(x, y);
8524
8525           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8526
8527           MovDelay[x][y] = 50;
8528
8529           /* !!! */
8530 #if 0
8531           RemoveField(newx, newy);
8532 #endif
8533           Feld[newx][newy] = EL_FLAMES;
8534           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8535           {
8536 #if 0
8537             RemoveField(newx1, newy1);
8538 #endif
8539             Feld[newx1][newy1] = EL_FLAMES;
8540           }
8541           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8542           {
8543 #if 0
8544             RemoveField(newx2, newy2);
8545 #endif
8546             Feld[newx2][newy2] = EL_FLAMES;
8547           }
8548
8549           return;
8550         }
8551       }
8552     }
8553     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8554              Feld[newx][newy] == EL_DIAMOND)
8555     {
8556       if (IS_MOVING(newx, newy))
8557         RemoveMovingField(newx, newy);
8558       else
8559       {
8560         Feld[newx][newy] = EL_EMPTY;
8561         TEST_DrawLevelField(newx, newy);
8562       }
8563
8564       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8565     }
8566     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8567              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8568     {
8569       if (AmoebaNr[newx][newy])
8570       {
8571         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8572         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8573             Feld[newx][newy] == EL_BD_AMOEBA)
8574           AmoebaCnt[AmoebaNr[newx][newy]]--;
8575       }
8576
8577 #if 0
8578       /* !!! test !!! */
8579       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8580       {
8581         RemoveMovingField(newx, newy);
8582       }
8583 #else
8584       if (IS_MOVING(newx, newy))
8585       {
8586         RemoveMovingField(newx, newy);
8587       }
8588 #endif
8589       else
8590       {
8591         Feld[newx][newy] = EL_EMPTY;
8592         TEST_DrawLevelField(newx, newy);
8593       }
8594
8595       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8596     }
8597     else if ((element == EL_PACMAN || element == EL_MOLE)
8598              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8599     {
8600       if (AmoebaNr[newx][newy])
8601       {
8602         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8603         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8604             Feld[newx][newy] == EL_BD_AMOEBA)
8605           AmoebaCnt[AmoebaNr[newx][newy]]--;
8606       }
8607
8608       if (element == EL_MOLE)
8609       {
8610         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8611         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8612
8613         ResetGfxAnimation(x, y);
8614         GfxAction[x][y] = ACTION_DIGGING;
8615         TEST_DrawLevelField(x, y);
8616
8617         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8618
8619         return;                         /* wait for shrinking amoeba */
8620       }
8621       else      /* element == EL_PACMAN */
8622       {
8623         Feld[newx][newy] = EL_EMPTY;
8624         TEST_DrawLevelField(newx, newy);
8625         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8626       }
8627     }
8628     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8629              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8630               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8631     {
8632       /* wait for shrinking amoeba to completely disappear */
8633       return;
8634     }
8635     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8636     {
8637       /* object was running against a wall */
8638
8639       TurnRound(x, y);
8640
8641 #if 0
8642       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8643       if (move_pattern & MV_ANY_DIRECTION &&
8644           move_pattern == MovDir[x][y])
8645       {
8646         int blocking_element =
8647           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8648
8649         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8650                                  MovDir[x][y]);
8651
8652         element = Feld[x][y];   /* element might have changed */
8653       }
8654 #endif
8655
8656       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8657         DrawLevelElementAnimation(x, y, element);
8658
8659       if (DONT_TOUCH(element))
8660         TestIfBadThingTouchesPlayer(x, y);
8661
8662       return;
8663     }
8664
8665     InitMovingField(x, y, MovDir[x][y]);
8666
8667     PlayLevelSoundAction(x, y, ACTION_MOVING);
8668   }
8669
8670   if (MovDir[x][y])
8671     ContinueMoving(x, y);
8672 }
8673
8674 void ContinueMoving(int x, int y)
8675 {
8676   int element = Feld[x][y];
8677   struct ElementInfo *ei = &element_info[element];
8678   int direction = MovDir[x][y];
8679   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8680   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8681   int newx = x + dx, newy = y + dy;
8682   int stored = Store[x][y];
8683   int stored_new = Store[newx][newy];
8684   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8685   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8686   boolean last_line = (newy == lev_fieldy - 1);
8687
8688   MovPos[x][y] += getElementMoveStepsize(x, y);
8689
8690   if (pushed_by_player) /* special case: moving object pushed by player */
8691     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8692
8693   if (ABS(MovPos[x][y]) < TILEX)
8694   {
8695 #if 0
8696     int ee = Feld[x][y];
8697     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8698     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8699
8700     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8701            x, y, ABS(MovPos[x][y]),
8702            ee, gg, ff,
8703            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8704 #endif
8705
8706     TEST_DrawLevelField(x, y);
8707
8708     return;     /* element is still moving */
8709   }
8710
8711   /* element reached destination field */
8712
8713   Feld[x][y] = EL_EMPTY;
8714   Feld[newx][newy] = element;
8715   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8716
8717   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8718   {
8719     element = Feld[newx][newy] = EL_ACID;
8720   }
8721   else if (element == EL_MOLE)
8722   {
8723     Feld[x][y] = EL_SAND;
8724
8725     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8726   }
8727   else if (element == EL_QUICKSAND_FILLING)
8728   {
8729     element = Feld[newx][newy] = get_next_element(element);
8730     Store[newx][newy] = Store[x][y];
8731   }
8732   else if (element == EL_QUICKSAND_EMPTYING)
8733   {
8734     Feld[x][y] = get_next_element(element);
8735     element = Feld[newx][newy] = Store[x][y];
8736   }
8737   else if (element == EL_QUICKSAND_FAST_FILLING)
8738   {
8739     element = Feld[newx][newy] = get_next_element(element);
8740     Store[newx][newy] = Store[x][y];
8741   }
8742   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8743   {
8744     Feld[x][y] = get_next_element(element);
8745     element = Feld[newx][newy] = Store[x][y];
8746   }
8747   else if (element == EL_MAGIC_WALL_FILLING)
8748   {
8749     element = Feld[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_MAGIC_WALL_EMPTYING)
8755   {
8756     Feld[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8759     element = Feld[newx][newy] = Store[x][y];
8760
8761 #if USE_NEW_CUSTOM_VALUE
8762     InitField(newx, newy, FALSE);
8763 #endif
8764   }
8765   else if (element == EL_BD_MAGIC_WALL_FILLING)
8766   {
8767     element = Feld[newx][newy] = get_next_element(element);
8768     if (!game.magic_wall_active)
8769       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8770     Store[newx][newy] = Store[x][y];
8771   }
8772   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8773   {
8774     Feld[x][y] = get_next_element(element);
8775     if (!game.magic_wall_active)
8776       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8777     element = Feld[newx][newy] = Store[x][y];
8778
8779 #if USE_NEW_CUSTOM_VALUE
8780     InitField(newx, newy, FALSE);
8781 #endif
8782   }
8783   else if (element == EL_DC_MAGIC_WALL_FILLING)
8784   {
8785     element = Feld[newx][newy] = get_next_element(element);
8786     if (!game.magic_wall_active)
8787       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8788     Store[newx][newy] = Store[x][y];
8789   }
8790   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8791   {
8792     Feld[x][y] = get_next_element(element);
8793     if (!game.magic_wall_active)
8794       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8795     element = Feld[newx][newy] = Store[x][y];
8796
8797 #if USE_NEW_CUSTOM_VALUE
8798     InitField(newx, newy, FALSE);
8799 #endif
8800   }
8801   else if (element == EL_AMOEBA_DROPPING)
8802   {
8803     Feld[x][y] = get_next_element(element);
8804     element = Feld[newx][newy] = Store[x][y];
8805   }
8806   else if (element == EL_SOKOBAN_OBJECT)
8807   {
8808     if (Back[x][y])
8809       Feld[x][y] = Back[x][y];
8810
8811     if (Back[newx][newy])
8812       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8813
8814     Back[x][y] = Back[newx][newy] = 0;
8815   }
8816
8817   Store[x][y] = EL_EMPTY;
8818   MovPos[x][y] = 0;
8819   MovDir[x][y] = 0;
8820   MovDelay[x][y] = 0;
8821
8822   MovDelay[newx][newy] = 0;
8823
8824   if (CAN_CHANGE_OR_HAS_ACTION(element))
8825   {
8826     /* copy element change control values to new field */
8827     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8828     ChangePage[newx][newy]  = ChangePage[x][y];
8829     ChangeCount[newx][newy] = ChangeCount[x][y];
8830     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8831   }
8832
8833 #if USE_NEW_CUSTOM_VALUE
8834   CustomValue[newx][newy] = CustomValue[x][y];
8835 #endif
8836
8837   ChangeDelay[x][y] = 0;
8838   ChangePage[x][y] = -1;
8839   ChangeCount[x][y] = 0;
8840   ChangeEvent[x][y] = -1;
8841
8842 #if USE_NEW_CUSTOM_VALUE
8843   CustomValue[x][y] = 0;
8844 #endif
8845
8846   /* copy animation control values to new field */
8847   GfxFrame[newx][newy]  = GfxFrame[x][y];
8848   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8849   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8850   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8851
8852   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8853
8854   /* some elements can leave other elements behind after moving */
8855 #if 1
8856   if (ei->move_leave_element != EL_EMPTY &&
8857       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8858       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8859 #else
8860   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8861       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8862       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8863 #endif
8864   {
8865     int move_leave_element = ei->move_leave_element;
8866
8867 #if 1
8868 #if 1
8869     /* this makes it possible to leave the removed element again */
8870     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8871       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8872 #else
8873     /* this makes it possible to leave the removed element again */
8874     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8875       move_leave_element = stored;
8876 #endif
8877 #else
8878     /* this makes it possible to leave the removed element again */
8879     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8880         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8881       move_leave_element = stored;
8882 #endif
8883
8884     Feld[x][y] = move_leave_element;
8885
8886     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8887       MovDir[x][y] = direction;
8888
8889     InitField(x, y, FALSE);
8890
8891     if (GFX_CRUMBLED(Feld[x][y]))
8892       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8893
8894     if (ELEM_IS_PLAYER(move_leave_element))
8895       RelocatePlayer(x, y, move_leave_element);
8896   }
8897
8898   /* do this after checking for left-behind element */
8899   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8900
8901   if (!CAN_MOVE(element) ||
8902       (CAN_FALL(element) && direction == MV_DOWN &&
8903        (element == EL_SPRING ||
8904         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8905         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8906     GfxDir[x][y] = MovDir[newx][newy] = 0;
8907
8908   TEST_DrawLevelField(x, y);
8909   TEST_DrawLevelField(newx, newy);
8910
8911   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8912
8913   /* prevent pushed element from moving on in pushed direction */
8914   if (pushed_by_player && CAN_MOVE(element) &&
8915       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8916       !(element_info[element].move_pattern & direction))
8917     TurnRound(newx, newy);
8918
8919   /* prevent elements on conveyor belt from moving on in last direction */
8920   if (pushed_by_conveyor && CAN_FALL(element) &&
8921       direction & MV_HORIZONTAL)
8922     MovDir[newx][newy] = 0;
8923
8924   if (!pushed_by_player)
8925   {
8926     int nextx = newx + dx, nexty = newy + dy;
8927     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8928
8929     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8930
8931     if (CAN_FALL(element) && direction == MV_DOWN)
8932       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8933
8934     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8935       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8936
8937 #if USE_FIX_IMPACT_COLLISION
8938     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8939       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8940 #endif
8941   }
8942
8943   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8944   {
8945     TestIfBadThingTouchesPlayer(newx, newy);
8946     TestIfBadThingTouchesFriend(newx, newy);
8947
8948     if (!IS_CUSTOM_ELEMENT(element))
8949       TestIfBadThingTouchesOtherBadThing(newx, newy);
8950   }
8951   else if (element == EL_PENGUIN)
8952     TestIfFriendTouchesBadThing(newx, newy);
8953
8954   if (DONT_GET_HIT_BY(element))
8955   {
8956     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8957   }
8958
8959   /* give the player one last chance (one more frame) to move away */
8960   if (CAN_FALL(element) && direction == MV_DOWN &&
8961       (last_line || (!IS_FREE(x, newy + 1) &&
8962                      (!IS_PLAYER(x, newy + 1) ||
8963                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8964     Impact(x, newy);
8965
8966   if (pushed_by_player && !game.use_change_when_pushing_bug)
8967   {
8968     int push_side = MV_DIR_OPPOSITE(direction);
8969     struct PlayerInfo *player = PLAYERINFO(x, y);
8970
8971     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8972                                player->index_bit, push_side);
8973     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8974                                         player->index_bit, push_side);
8975   }
8976
8977   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8978     MovDelay[newx][newy] = 1;
8979
8980   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8981
8982   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8983
8984 #if 0
8985   if (ChangePage[newx][newy] != -1)             /* delayed change */
8986   {
8987     int page = ChangePage[newx][newy];
8988     struct ElementChangeInfo *change = &ei->change_page[page];
8989
8990     ChangePage[newx][newy] = -1;
8991
8992     if (change->can_change)
8993     {
8994       if (ChangeElement(newx, newy, element, page))
8995       {
8996         if (change->post_change_function)
8997           change->post_change_function(newx, newy);
8998       }
8999     }
9000
9001     if (change->has_action)
9002       ExecuteCustomElementAction(newx, newy, element, page);
9003   }
9004 #endif
9005
9006   TestIfElementHitsCustomElement(newx, newy, direction);
9007   TestIfPlayerTouchesCustomElement(newx, newy);
9008   TestIfElementTouchesCustomElement(newx, newy);
9009
9010   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9011       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9012     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9013                              MV_DIR_OPPOSITE(direction));
9014 }
9015
9016 int AmoebeNachbarNr(int ax, int ay)
9017 {
9018   int i;
9019   int element = Feld[ax][ay];
9020   int group_nr = 0;
9021   static int xy[4][2] =
9022   {
9023     { 0, -1 },
9024     { -1, 0 },
9025     { +1, 0 },
9026     { 0, +1 }
9027   };
9028
9029   for (i = 0; i < NUM_DIRECTIONS; i++)
9030   {
9031     int x = ax + xy[i][0];
9032     int y = ay + xy[i][1];
9033
9034     if (!IN_LEV_FIELD(x, y))
9035       continue;
9036
9037     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9038       group_nr = AmoebaNr[x][y];
9039   }
9040
9041   return group_nr;
9042 }
9043
9044 void AmoebenVereinigen(int ax, int ay)
9045 {
9046   int i, x, y, xx, yy;
9047   int new_group_nr = AmoebaNr[ax][ay];
9048   static int xy[4][2] =
9049   {
9050     { 0, -1 },
9051     { -1, 0 },
9052     { +1, 0 },
9053     { 0, +1 }
9054   };
9055
9056   if (new_group_nr == 0)
9057     return;
9058
9059   for (i = 0; i < NUM_DIRECTIONS; i++)
9060   {
9061     x = ax + xy[i][0];
9062     y = ay + xy[i][1];
9063
9064     if (!IN_LEV_FIELD(x, y))
9065       continue;
9066
9067     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9068          Feld[x][y] == EL_BD_AMOEBA ||
9069          Feld[x][y] == EL_AMOEBA_DEAD) &&
9070         AmoebaNr[x][y] != new_group_nr)
9071     {
9072       int old_group_nr = AmoebaNr[x][y];
9073
9074       if (old_group_nr == 0)
9075         return;
9076
9077       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9078       AmoebaCnt[old_group_nr] = 0;
9079       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9080       AmoebaCnt2[old_group_nr] = 0;
9081
9082       SCAN_PLAYFIELD(xx, yy)
9083       {
9084         if (AmoebaNr[xx][yy] == old_group_nr)
9085           AmoebaNr[xx][yy] = new_group_nr;
9086       }
9087     }
9088   }
9089 }
9090
9091 void AmoebeUmwandeln(int ax, int ay)
9092 {
9093   int i, x, y;
9094
9095   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9096   {
9097     int group_nr = AmoebaNr[ax][ay];
9098
9099 #ifdef DEBUG
9100     if (group_nr == 0)
9101     {
9102       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9103       printf("AmoebeUmwandeln(): This should never happen!\n");
9104       return;
9105     }
9106 #endif
9107
9108     SCAN_PLAYFIELD(x, y)
9109     {
9110       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9111       {
9112         AmoebaNr[x][y] = 0;
9113         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9114       }
9115     }
9116
9117     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9118                             SND_AMOEBA_TURNING_TO_GEM :
9119                             SND_AMOEBA_TURNING_TO_ROCK));
9120     Bang(ax, ay);
9121   }
9122   else
9123   {
9124     static int xy[4][2] =
9125     {
9126       { 0, -1 },
9127       { -1, 0 },
9128       { +1, 0 },
9129       { 0, +1 }
9130     };
9131
9132     for (i = 0; i < NUM_DIRECTIONS; i++)
9133     {
9134       x = ax + xy[i][0];
9135       y = ay + xy[i][1];
9136
9137       if (!IN_LEV_FIELD(x, y))
9138         continue;
9139
9140       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9141       {
9142         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9143                               SND_AMOEBA_TURNING_TO_GEM :
9144                               SND_AMOEBA_TURNING_TO_ROCK));
9145         Bang(x, y);
9146       }
9147     }
9148   }
9149 }
9150
9151 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9152 {
9153   int x, y;
9154   int group_nr = AmoebaNr[ax][ay];
9155   boolean done = FALSE;
9156
9157 #ifdef DEBUG
9158   if (group_nr == 0)
9159   {
9160     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9161     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9162     return;
9163   }
9164 #endif
9165
9166   SCAN_PLAYFIELD(x, y)
9167   {
9168     if (AmoebaNr[x][y] == group_nr &&
9169         (Feld[x][y] == EL_AMOEBA_DEAD ||
9170          Feld[x][y] == EL_BD_AMOEBA ||
9171          Feld[x][y] == EL_AMOEBA_GROWING))
9172     {
9173       AmoebaNr[x][y] = 0;
9174       Feld[x][y] = new_element;
9175       InitField(x, y, FALSE);
9176       TEST_DrawLevelField(x, y);
9177       done = TRUE;
9178     }
9179   }
9180
9181   if (done)
9182     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9183                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9184                             SND_BD_AMOEBA_TURNING_TO_GEM));
9185 }
9186
9187 void AmoebeWaechst(int x, int y)
9188 {
9189   static unsigned long sound_delay = 0;
9190   static unsigned long sound_delay_value = 0;
9191
9192   if (!MovDelay[x][y])          /* start new growing cycle */
9193   {
9194     MovDelay[x][y] = 7;
9195
9196     if (DelayReached(&sound_delay, sound_delay_value))
9197     {
9198       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9199       sound_delay_value = 30;
9200     }
9201   }
9202
9203   if (MovDelay[x][y])           /* wait some time before growing bigger */
9204   {
9205     MovDelay[x][y]--;
9206     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9207     {
9208       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9209                                            6 - MovDelay[x][y]);
9210
9211       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9212     }
9213
9214     if (!MovDelay[x][y])
9215     {
9216       Feld[x][y] = Store[x][y];
9217       Store[x][y] = 0;
9218       TEST_DrawLevelField(x, y);
9219     }
9220   }
9221 }
9222
9223 void AmoebaDisappearing(int x, int y)
9224 {
9225   static unsigned long sound_delay = 0;
9226   static unsigned long sound_delay_value = 0;
9227
9228   if (!MovDelay[x][y])          /* start new shrinking cycle */
9229   {
9230     MovDelay[x][y] = 7;
9231
9232     if (DelayReached(&sound_delay, sound_delay_value))
9233       sound_delay_value = 30;
9234   }
9235
9236   if (MovDelay[x][y])           /* wait some time before shrinking */
9237   {
9238     MovDelay[x][y]--;
9239     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9240     {
9241       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9242                                            6 - MovDelay[x][y]);
9243
9244       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9245     }
9246
9247     if (!MovDelay[x][y])
9248     {
9249       Feld[x][y] = EL_EMPTY;
9250       TEST_DrawLevelField(x, y);
9251
9252       /* don't let mole enter this field in this cycle;
9253          (give priority to objects falling to this field from above) */
9254       Stop[x][y] = TRUE;
9255     }
9256   }
9257 }
9258
9259 void AmoebeAbleger(int ax, int ay)
9260 {
9261   int i;
9262   int element = Feld[ax][ay];
9263   int graphic = el2img(element);
9264   int newax = ax, neway = ay;
9265   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9266   static int xy[4][2] =
9267   {
9268     { 0, -1 },
9269     { -1, 0 },
9270     { +1, 0 },
9271     { 0, +1 }
9272   };
9273
9274   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9275   {
9276     Feld[ax][ay] = EL_AMOEBA_DEAD;
9277     TEST_DrawLevelField(ax, ay);
9278     return;
9279   }
9280
9281   if (IS_ANIMATED(graphic))
9282     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9283
9284   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9285     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9286
9287   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9288   {
9289     MovDelay[ax][ay]--;
9290     if (MovDelay[ax][ay])
9291       return;
9292   }
9293
9294   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9295   {
9296     int start = RND(4);
9297     int x = ax + xy[start][0];
9298     int y = ay + xy[start][1];
9299
9300     if (!IN_LEV_FIELD(x, y))
9301       return;
9302
9303     if (IS_FREE(x, y) ||
9304         CAN_GROW_INTO(Feld[x][y]) ||
9305         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9306         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9307     {
9308       newax = x;
9309       neway = y;
9310     }
9311
9312     if (newax == ax && neway == ay)
9313       return;
9314   }
9315   else                          /* normal or "filled" (BD style) amoeba */
9316   {
9317     int start = RND(4);
9318     boolean waiting_for_player = FALSE;
9319
9320     for (i = 0; i < NUM_DIRECTIONS; i++)
9321     {
9322       int j = (start + i) % 4;
9323       int x = ax + xy[j][0];
9324       int y = ay + xy[j][1];
9325
9326       if (!IN_LEV_FIELD(x, y))
9327         continue;
9328
9329       if (IS_FREE(x, y) ||
9330           CAN_GROW_INTO(Feld[x][y]) ||
9331           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9332           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9333       {
9334         newax = x;
9335         neway = y;
9336         break;
9337       }
9338       else if (IS_PLAYER(x, y))
9339         waiting_for_player = TRUE;
9340     }
9341
9342     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9343     {
9344       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9345       {
9346         Feld[ax][ay] = EL_AMOEBA_DEAD;
9347         TEST_DrawLevelField(ax, ay);
9348         AmoebaCnt[AmoebaNr[ax][ay]]--;
9349
9350         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9351         {
9352           if (element == EL_AMOEBA_FULL)
9353             AmoebeUmwandeln(ax, ay);
9354           else if (element == EL_BD_AMOEBA)
9355             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9356         }
9357       }
9358       return;
9359     }
9360     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9361     {
9362       /* amoeba gets larger by growing in some direction */
9363
9364       int new_group_nr = AmoebaNr[ax][ay];
9365
9366 #ifdef DEBUG
9367   if (new_group_nr == 0)
9368   {
9369     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9370     printf("AmoebeAbleger(): This should never happen!\n");
9371     return;
9372   }
9373 #endif
9374
9375       AmoebaNr[newax][neway] = new_group_nr;
9376       AmoebaCnt[new_group_nr]++;
9377       AmoebaCnt2[new_group_nr]++;
9378
9379       /* if amoeba touches other amoeba(s) after growing, unify them */
9380       AmoebenVereinigen(newax, neway);
9381
9382       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9383       {
9384         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9385         return;
9386       }
9387     }
9388   }
9389
9390   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9391       (neway == lev_fieldy - 1 && newax != ax))
9392   {
9393     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9394     Store[newax][neway] = element;
9395   }
9396   else if (neway == ay || element == EL_EMC_DRIPPER)
9397   {
9398     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9399
9400     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9401   }
9402   else
9403   {
9404     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9405     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9406     Store[ax][ay] = EL_AMOEBA_DROP;
9407     ContinueMoving(ax, ay);
9408     return;
9409   }
9410
9411   TEST_DrawLevelField(newax, neway);
9412 }
9413
9414 void Life(int ax, int ay)
9415 {
9416   int x1, y1, x2, y2;
9417   int life_time = 40;
9418   int element = Feld[ax][ay];
9419   int graphic = el2img(element);
9420   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9421                          level.biomaze);
9422   boolean changed = FALSE;
9423
9424   if (IS_ANIMATED(graphic))
9425     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9426
9427   if (Stop[ax][ay])
9428     return;
9429
9430   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9431     MovDelay[ax][ay] = life_time;
9432
9433   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9434   {
9435     MovDelay[ax][ay]--;
9436     if (MovDelay[ax][ay])
9437       return;
9438   }
9439
9440   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9441   {
9442     int xx = ax+x1, yy = ay+y1;
9443     int nachbarn = 0;
9444
9445     if (!IN_LEV_FIELD(xx, yy))
9446       continue;
9447
9448     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9449     {
9450       int x = xx+x2, y = yy+y2;
9451
9452       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9453         continue;
9454
9455       if (((Feld[x][y] == element ||
9456             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9457            !Stop[x][y]) ||
9458           (IS_FREE(x, y) && Stop[x][y]))
9459         nachbarn++;
9460     }
9461
9462     if (xx == ax && yy == ay)           /* field in the middle */
9463     {
9464       if (nachbarn < life_parameter[0] ||
9465           nachbarn > life_parameter[1])
9466       {
9467         Feld[xx][yy] = EL_EMPTY;
9468         if (!Stop[xx][yy])
9469           TEST_DrawLevelField(xx, yy);
9470         Stop[xx][yy] = TRUE;
9471         changed = TRUE;
9472       }
9473     }
9474     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9475     {                                   /* free border field */
9476       if (nachbarn >= life_parameter[2] &&
9477           nachbarn <= life_parameter[3])
9478       {
9479         Feld[xx][yy] = element;
9480         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9481         if (!Stop[xx][yy])
9482           TEST_DrawLevelField(xx, yy);
9483         Stop[xx][yy] = TRUE;
9484         changed = TRUE;
9485       }
9486     }
9487   }
9488
9489   if (changed)
9490     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9491                    SND_GAME_OF_LIFE_GROWING);
9492 }
9493
9494 static void InitRobotWheel(int x, int y)
9495 {
9496   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9497 }
9498
9499 static void RunRobotWheel(int x, int y)
9500 {
9501   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9502 }
9503
9504 static void StopRobotWheel(int x, int y)
9505 {
9506   if (ZX == x && ZY == y)
9507   {
9508     ZX = ZY = -1;
9509
9510     game.robot_wheel_active = FALSE;
9511   }
9512 }
9513
9514 static void InitTimegateWheel(int x, int y)
9515 {
9516   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9517 }
9518
9519 static void RunTimegateWheel(int x, int y)
9520 {
9521   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9522 }
9523
9524 static void InitMagicBallDelay(int x, int y)
9525 {
9526 #if 1
9527   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9528 #else
9529   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9530 #endif
9531 }
9532
9533 static void ActivateMagicBall(int bx, int by)
9534 {
9535   int x, y;
9536
9537   if (level.ball_random)
9538   {
9539     int pos_border = RND(8);    /* select one of the eight border elements */
9540     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9541     int xx = pos_content % 3;
9542     int yy = pos_content / 3;
9543
9544     x = bx - 1 + xx;
9545     y = by - 1 + yy;
9546
9547     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9548       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9549   }
9550   else
9551   {
9552     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9553     {
9554       int xx = x - bx + 1;
9555       int yy = y - by + 1;
9556
9557       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9558         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9559     }
9560   }
9561
9562   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9563 }
9564
9565 void CheckExit(int x, int y)
9566 {
9567   if (local_player->gems_still_needed > 0 ||
9568       local_player->sokobanfields_still_needed > 0 ||
9569       local_player->lights_still_needed > 0)
9570   {
9571     int element = Feld[x][y];
9572     int graphic = el2img(element);
9573
9574     if (IS_ANIMATED(graphic))
9575       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9576
9577     return;
9578   }
9579
9580   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9581     return;
9582
9583   Feld[x][y] = EL_EXIT_OPENING;
9584
9585   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9586 }
9587
9588 void CheckExitEM(int x, int y)
9589 {
9590   if (local_player->gems_still_needed > 0 ||
9591       local_player->sokobanfields_still_needed > 0 ||
9592       local_player->lights_still_needed > 0)
9593   {
9594     int element = Feld[x][y];
9595     int graphic = el2img(element);
9596
9597     if (IS_ANIMATED(graphic))
9598       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9599
9600     return;
9601   }
9602
9603   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9604     return;
9605
9606   Feld[x][y] = EL_EM_EXIT_OPENING;
9607
9608   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9609 }
9610
9611 void CheckExitSteel(int x, int y)
9612 {
9613   if (local_player->gems_still_needed > 0 ||
9614       local_player->sokobanfields_still_needed > 0 ||
9615       local_player->lights_still_needed > 0)
9616   {
9617     int element = Feld[x][y];
9618     int graphic = el2img(element);
9619
9620     if (IS_ANIMATED(graphic))
9621       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9622
9623     return;
9624   }
9625
9626   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9627     return;
9628
9629   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9630
9631   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9632 }
9633
9634 void CheckExitSteelEM(int x, int y)
9635 {
9636   if (local_player->gems_still_needed > 0 ||
9637       local_player->sokobanfields_still_needed > 0 ||
9638       local_player->lights_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_EM_STEEL_EXIT_OPENING;
9653
9654   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9655 }
9656
9657 void CheckExitSP(int x, int y)
9658 {
9659   if (local_player->gems_still_needed > 0)
9660   {
9661     int element = Feld[x][y];
9662     int graphic = el2img(element);
9663
9664     if (IS_ANIMATED(graphic))
9665       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9666
9667     return;
9668   }
9669
9670   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9671     return;
9672
9673   Feld[x][y] = EL_SP_EXIT_OPENING;
9674
9675   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9676 }
9677
9678 static void CloseAllOpenTimegates()
9679 {
9680   int x, y;
9681
9682   SCAN_PLAYFIELD(x, y)
9683   {
9684     int element = Feld[x][y];
9685
9686     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9687     {
9688       Feld[x][y] = EL_TIMEGATE_CLOSING;
9689
9690       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9691     }
9692   }
9693 }
9694
9695 void DrawTwinkleOnField(int x, int y)
9696 {
9697   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9698     return;
9699
9700   if (Feld[x][y] == EL_BD_DIAMOND)
9701     return;
9702
9703   if (MovDelay[x][y] == 0)      /* next animation frame */
9704     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9705
9706   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9707   {
9708     MovDelay[x][y]--;
9709
9710     DrawLevelElementAnimation(x, y, Feld[x][y]);
9711
9712     if (MovDelay[x][y] != 0)
9713     {
9714       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9715                                            10 - MovDelay[x][y]);
9716
9717       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9718     }
9719   }
9720 }
9721
9722 void MauerWaechst(int x, int y)
9723 {
9724   int delay = 6;
9725
9726   if (!MovDelay[x][y])          /* next animation frame */
9727     MovDelay[x][y] = 3 * delay;
9728
9729   if (MovDelay[x][y])           /* wait some time before next frame */
9730   {
9731     MovDelay[x][y]--;
9732
9733     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9734     {
9735       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9736       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9737
9738       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9739     }
9740
9741     if (!MovDelay[x][y])
9742     {
9743       if (MovDir[x][y] == MV_LEFT)
9744       {
9745         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9746           TEST_DrawLevelField(x - 1, y);
9747       }
9748       else if (MovDir[x][y] == MV_RIGHT)
9749       {
9750         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9751           TEST_DrawLevelField(x + 1, y);
9752       }
9753       else if (MovDir[x][y] == MV_UP)
9754       {
9755         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9756           TEST_DrawLevelField(x, y - 1);
9757       }
9758       else
9759       {
9760         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9761           TEST_DrawLevelField(x, y + 1);
9762       }
9763
9764       Feld[x][y] = Store[x][y];
9765       Store[x][y] = 0;
9766       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9767       TEST_DrawLevelField(x, y);
9768     }
9769   }
9770 }
9771
9772 void MauerAbleger(int ax, int ay)
9773 {
9774   int element = Feld[ax][ay];
9775   int graphic = el2img(element);
9776   boolean oben_frei = FALSE, unten_frei = FALSE;
9777   boolean links_frei = FALSE, rechts_frei = FALSE;
9778   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9779   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9780   boolean new_wall = FALSE;
9781
9782   if (IS_ANIMATED(graphic))
9783     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9784
9785   if (!MovDelay[ax][ay])        /* start building new wall */
9786     MovDelay[ax][ay] = 6;
9787
9788   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9789   {
9790     MovDelay[ax][ay]--;
9791     if (MovDelay[ax][ay])
9792       return;
9793   }
9794
9795   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9796     oben_frei = TRUE;
9797   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9798     unten_frei = TRUE;
9799   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9800     links_frei = TRUE;
9801   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9802     rechts_frei = TRUE;
9803
9804   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9805       element == EL_EXPANDABLE_WALL_ANY)
9806   {
9807     if (oben_frei)
9808     {
9809       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9810       Store[ax][ay-1] = element;
9811       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9812       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9813         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9814                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9815       new_wall = TRUE;
9816     }
9817     if (unten_frei)
9818     {
9819       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9820       Store[ax][ay+1] = element;
9821       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9822       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9823         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9824                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9825       new_wall = TRUE;
9826     }
9827   }
9828
9829   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9830       element == EL_EXPANDABLE_WALL_ANY ||
9831       element == EL_EXPANDABLE_WALL ||
9832       element == EL_BD_EXPANDABLE_WALL)
9833   {
9834     if (links_frei)
9835     {
9836       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9837       Store[ax-1][ay] = element;
9838       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9839       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9840         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9841                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9842       new_wall = TRUE;
9843     }
9844
9845     if (rechts_frei)
9846     {
9847       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9848       Store[ax+1][ay] = element;
9849       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9850       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9851         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9852                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9853       new_wall = TRUE;
9854     }
9855   }
9856
9857   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9858     TEST_DrawLevelField(ax, ay);
9859
9860   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9861     oben_massiv = TRUE;
9862   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9863     unten_massiv = TRUE;
9864   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9865     links_massiv = TRUE;
9866   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9867     rechts_massiv = TRUE;
9868
9869   if (((oben_massiv && unten_massiv) ||
9870        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9871        element == EL_EXPANDABLE_WALL) &&
9872       ((links_massiv && rechts_massiv) ||
9873        element == EL_EXPANDABLE_WALL_VERTICAL))
9874     Feld[ax][ay] = EL_WALL;
9875
9876   if (new_wall)
9877     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9878 }
9879
9880 void MauerAblegerStahl(int ax, int ay)
9881 {
9882   int element = Feld[ax][ay];
9883   int graphic = el2img(element);
9884   boolean oben_frei = FALSE, unten_frei = FALSE;
9885   boolean links_frei = FALSE, rechts_frei = FALSE;
9886   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9887   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9888   boolean new_wall = FALSE;
9889
9890   if (IS_ANIMATED(graphic))
9891     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9892
9893   if (!MovDelay[ax][ay])        /* start building new wall */
9894     MovDelay[ax][ay] = 6;
9895
9896   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9897   {
9898     MovDelay[ax][ay]--;
9899     if (MovDelay[ax][ay])
9900       return;
9901   }
9902
9903   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9904     oben_frei = TRUE;
9905   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9906     unten_frei = TRUE;
9907   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9908     links_frei = TRUE;
9909   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9910     rechts_frei = TRUE;
9911
9912   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9913       element == EL_EXPANDABLE_STEELWALL_ANY)
9914   {
9915     if (oben_frei)
9916     {
9917       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9918       Store[ax][ay-1] = element;
9919       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9920       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9921         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9922                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9923       new_wall = TRUE;
9924     }
9925     if (unten_frei)
9926     {
9927       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9928       Store[ax][ay+1] = element;
9929       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9930       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9931         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9932                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9933       new_wall = TRUE;
9934     }
9935   }
9936
9937   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9938       element == EL_EXPANDABLE_STEELWALL_ANY)
9939   {
9940     if (links_frei)
9941     {
9942       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9943       Store[ax-1][ay] = element;
9944       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9945       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9946         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9947                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9948       new_wall = TRUE;
9949     }
9950
9951     if (rechts_frei)
9952     {
9953       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9954       Store[ax+1][ay] = element;
9955       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9956       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9957         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9958                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9959       new_wall = TRUE;
9960     }
9961   }
9962
9963   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9964     oben_massiv = TRUE;
9965   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9966     unten_massiv = TRUE;
9967   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9968     links_massiv = TRUE;
9969   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9970     rechts_massiv = TRUE;
9971
9972   if (((oben_massiv && unten_massiv) ||
9973        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9974       ((links_massiv && rechts_massiv) ||
9975        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9976     Feld[ax][ay] = EL_STEELWALL;
9977
9978   if (new_wall)
9979     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9980 }
9981
9982 void CheckForDragon(int x, int y)
9983 {
9984   int i, j;
9985   boolean dragon_found = FALSE;
9986   static int xy[4][2] =
9987   {
9988     { 0, -1 },
9989     { -1, 0 },
9990     { +1, 0 },
9991     { 0, +1 }
9992   };
9993
9994   for (i = 0; i < NUM_DIRECTIONS; i++)
9995   {
9996     for (j = 0; j < 4; j++)
9997     {
9998       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9999
10000       if (IN_LEV_FIELD(xx, yy) &&
10001           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10002       {
10003         if (Feld[xx][yy] == EL_DRAGON)
10004           dragon_found = TRUE;
10005       }
10006       else
10007         break;
10008     }
10009   }
10010
10011   if (!dragon_found)
10012   {
10013     for (i = 0; i < NUM_DIRECTIONS; i++)
10014     {
10015       for (j = 0; j < 3; j++)
10016       {
10017         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10018   
10019         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10020         {
10021           Feld[xx][yy] = EL_EMPTY;
10022           TEST_DrawLevelField(xx, yy);
10023         }
10024         else
10025           break;
10026       }
10027     }
10028   }
10029 }
10030
10031 static void InitBuggyBase(int x, int y)
10032 {
10033   int element = Feld[x][y];
10034   int activating_delay = FRAMES_PER_SECOND / 4;
10035
10036   ChangeDelay[x][y] =
10037     (element == EL_SP_BUGGY_BASE ?
10038      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10039      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10040      activating_delay :
10041      element == EL_SP_BUGGY_BASE_ACTIVE ?
10042      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10043 }
10044
10045 static void WarnBuggyBase(int x, int y)
10046 {
10047   int i;
10048   static int xy[4][2] =
10049   {
10050     { 0, -1 },
10051     { -1, 0 },
10052     { +1, 0 },
10053     { 0, +1 }
10054   };
10055
10056   for (i = 0; i < NUM_DIRECTIONS; i++)
10057   {
10058     int xx = x + xy[i][0];
10059     int yy = y + xy[i][1];
10060
10061     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10062     {
10063       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10064
10065       break;
10066     }
10067   }
10068 }
10069
10070 static void InitTrap(int x, int y)
10071 {
10072   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10073 }
10074
10075 static void ActivateTrap(int x, int y)
10076 {
10077   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10078 }
10079
10080 static void ChangeActiveTrap(int x, int y)
10081 {
10082   int graphic = IMG_TRAP_ACTIVE;
10083
10084   /* if new animation frame was drawn, correct crumbled sand border */
10085   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10086     TEST_DrawLevelFieldCrumbledSand(x, y);
10087 }
10088
10089 static int getSpecialActionElement(int element, int number, int base_element)
10090 {
10091   return (element != EL_EMPTY ? element :
10092           number != -1 ? base_element + number - 1 :
10093           EL_EMPTY);
10094 }
10095
10096 static int getModifiedActionNumber(int value_old, int operator, int operand,
10097                                    int value_min, int value_max)
10098 {
10099   int value_new = (operator == CA_MODE_SET      ? operand :
10100                    operator == CA_MODE_ADD      ? value_old + operand :
10101                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10102                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10103                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10104                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10105                    value_old);
10106
10107   return (value_new < value_min ? value_min :
10108           value_new > value_max ? value_max :
10109           value_new);
10110 }
10111
10112 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10113 {
10114   struct ElementInfo *ei = &element_info[element];
10115   struct ElementChangeInfo *change = &ei->change_page[page];
10116   int target_element = change->target_element;
10117   int action_type = change->action_type;
10118   int action_mode = change->action_mode;
10119   int action_arg = change->action_arg;
10120   int i;
10121
10122   if (!change->has_action)
10123     return;
10124
10125   /* ---------- determine action paramater values -------------------------- */
10126
10127   int level_time_value =
10128     (level.time > 0 ? TimeLeft :
10129      TimePlayed);
10130
10131   int action_arg_element =
10132     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10133      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10134      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10135      EL_EMPTY);
10136
10137   int action_arg_direction =
10138     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10139      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10140      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10141      change->actual_trigger_side :
10142      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10143      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10144      MV_NONE);
10145
10146   int action_arg_number_min =
10147     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10148      CA_ARG_MIN);
10149
10150   int action_arg_number_max =
10151     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10152      action_type == CA_SET_LEVEL_GEMS ? 999 :
10153      action_type == CA_SET_LEVEL_TIME ? 9999 :
10154      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10155      action_type == CA_SET_CE_VALUE ? 9999 :
10156      action_type == CA_SET_CE_SCORE ? 9999 :
10157      CA_ARG_MAX);
10158
10159   int action_arg_number_reset =
10160     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10161      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10162      action_type == CA_SET_LEVEL_TIME ? level.time :
10163      action_type == CA_SET_LEVEL_SCORE ? 0 :
10164 #if USE_NEW_CUSTOM_VALUE
10165      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10166 #else
10167      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10168 #endif
10169      action_type == CA_SET_CE_SCORE ? 0 :
10170      0);
10171
10172   int action_arg_number =
10173     (action_arg <= CA_ARG_MAX ? action_arg :
10174      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10175      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10176      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10177      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10178      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10179      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10180 #if USE_NEW_CUSTOM_VALUE
10181      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10182 #else
10183      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10184 #endif
10185      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10186      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10187      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10188      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10189      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10190      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10191      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10192      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10193      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10194      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10195      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10196      -1);
10197
10198   int action_arg_number_old =
10199     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10200      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10201      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10202      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10203      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10204      0);
10205
10206   int action_arg_number_new =
10207     getModifiedActionNumber(action_arg_number_old,
10208                             action_mode, action_arg_number,
10209                             action_arg_number_min, action_arg_number_max);
10210
10211 #if 1
10212   int trigger_player_bits = change->actual_trigger_player_bits;
10213 #else
10214   int trigger_player_bits =
10215     (change->actual_trigger_player >= EL_PLAYER_1 &&
10216      change->actual_trigger_player <= EL_PLAYER_4 ?
10217      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10218      PLAYER_BITS_ANY);
10219 #endif
10220
10221   int action_arg_player_bits =
10222     (action_arg >= CA_ARG_PLAYER_1 &&
10223      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10224      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10225      PLAYER_BITS_ANY);
10226
10227   /* ---------- execute action  -------------------------------------------- */
10228
10229   switch (action_type)
10230   {
10231     case CA_NO_ACTION:
10232     {
10233       return;
10234     }
10235
10236     /* ---------- level actions  ------------------------------------------- */
10237
10238     case CA_RESTART_LEVEL:
10239     {
10240       game.restart_level = TRUE;
10241
10242       break;
10243     }
10244
10245     case CA_SHOW_ENVELOPE:
10246     {
10247       int element = getSpecialActionElement(action_arg_element,
10248                                             action_arg_number, EL_ENVELOPE_1);
10249
10250       if (IS_ENVELOPE(element))
10251         local_player->show_envelope = element;
10252
10253       break;
10254     }
10255
10256     case CA_SET_LEVEL_TIME:
10257     {
10258       if (level.time > 0)       /* only modify limited time value */
10259       {
10260         TimeLeft = action_arg_number_new;
10261
10262 #if 1
10263         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10264
10265         DisplayGameControlValues();
10266 #else
10267         DrawGameValue_Time(TimeLeft);
10268 #endif
10269
10270         if (!TimeLeft && setup.time_limit)
10271           for (i = 0; i < MAX_PLAYERS; i++)
10272             KillPlayer(&stored_player[i]);
10273       }
10274
10275       break;
10276     }
10277
10278     case CA_SET_LEVEL_SCORE:
10279     {
10280       local_player->score = action_arg_number_new;
10281
10282 #if 1
10283       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10284
10285       DisplayGameControlValues();
10286 #else
10287       DrawGameValue_Score(local_player->score);
10288 #endif
10289
10290       break;
10291     }
10292
10293     case CA_SET_LEVEL_GEMS:
10294     {
10295       local_player->gems_still_needed = action_arg_number_new;
10296
10297 #if 1
10298       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10299
10300       DisplayGameControlValues();
10301 #else
10302       DrawGameValue_Emeralds(local_player->gems_still_needed);
10303 #endif
10304
10305       break;
10306     }
10307
10308 #if !USE_PLAYER_GRAVITY
10309     case CA_SET_LEVEL_GRAVITY:
10310     {
10311       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10312                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10313                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10314                       game.gravity);
10315       break;
10316     }
10317 #endif
10318
10319     case CA_SET_LEVEL_WIND:
10320     {
10321       game.wind_direction = action_arg_direction;
10322
10323       break;
10324     }
10325
10326     /* ---------- player actions  ------------------------------------------ */
10327
10328     case CA_MOVE_PLAYER:
10329     {
10330       /* automatically move to the next field in specified direction */
10331       for (i = 0; i < MAX_PLAYERS; i++)
10332         if (trigger_player_bits & (1 << i))
10333           stored_player[i].programmed_action = action_arg_direction;
10334
10335       break;
10336     }
10337
10338     case CA_EXIT_PLAYER:
10339     {
10340       for (i = 0; i < MAX_PLAYERS; i++)
10341         if (action_arg_player_bits & (1 << i))
10342           PlayerWins(&stored_player[i]);
10343
10344       break;
10345     }
10346
10347     case CA_KILL_PLAYER:
10348     {
10349       for (i = 0; i < MAX_PLAYERS; i++)
10350         if (action_arg_player_bits & (1 << i))
10351           KillPlayer(&stored_player[i]);
10352
10353       break;
10354     }
10355
10356     case CA_SET_PLAYER_KEYS:
10357     {
10358       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10359       int element = getSpecialActionElement(action_arg_element,
10360                                             action_arg_number, EL_KEY_1);
10361
10362       if (IS_KEY(element))
10363       {
10364         for (i = 0; i < MAX_PLAYERS; i++)
10365         {
10366           if (trigger_player_bits & (1 << i))
10367           {
10368             stored_player[i].key[KEY_NR(element)] = key_state;
10369
10370             DrawGameDoorValues();
10371           }
10372         }
10373       }
10374
10375       break;
10376     }
10377
10378     case CA_SET_PLAYER_SPEED:
10379     {
10380       for (i = 0; i < MAX_PLAYERS; i++)
10381       {
10382         if (trigger_player_bits & (1 << i))
10383         {
10384           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10385
10386           if (action_arg == CA_ARG_SPEED_FASTER &&
10387               stored_player[i].cannot_move)
10388           {
10389             action_arg_number = STEPSIZE_VERY_SLOW;
10390           }
10391           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10392                    action_arg == CA_ARG_SPEED_FASTER)
10393           {
10394             action_arg_number = 2;
10395             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10396                            CA_MODE_MULTIPLY);
10397           }
10398           else if (action_arg == CA_ARG_NUMBER_RESET)
10399           {
10400             action_arg_number = level.initial_player_stepsize[i];
10401           }
10402
10403           move_stepsize =
10404             getModifiedActionNumber(move_stepsize,
10405                                     action_mode,
10406                                     action_arg_number,
10407                                     action_arg_number_min,
10408                                     action_arg_number_max);
10409
10410           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10411         }
10412       }
10413
10414       break;
10415     }
10416
10417     case CA_SET_PLAYER_SHIELD:
10418     {
10419       for (i = 0; i < MAX_PLAYERS; i++)
10420       {
10421         if (trigger_player_bits & (1 << i))
10422         {
10423           if (action_arg == CA_ARG_SHIELD_OFF)
10424           {
10425             stored_player[i].shield_normal_time_left = 0;
10426             stored_player[i].shield_deadly_time_left = 0;
10427           }
10428           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10429           {
10430             stored_player[i].shield_normal_time_left = 999999;
10431           }
10432           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10433           {
10434             stored_player[i].shield_normal_time_left = 999999;
10435             stored_player[i].shield_deadly_time_left = 999999;
10436           }
10437         }
10438       }
10439
10440       break;
10441     }
10442
10443 #if USE_PLAYER_GRAVITY
10444     case CA_SET_PLAYER_GRAVITY:
10445     {
10446       for (i = 0; i < MAX_PLAYERS; i++)
10447       {
10448         if (trigger_player_bits & (1 << i))
10449         {
10450           stored_player[i].gravity =
10451             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10452              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10453              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10454              stored_player[i].gravity);
10455         }
10456       }
10457
10458       break;
10459     }
10460 #endif
10461
10462     case CA_SET_PLAYER_ARTWORK:
10463     {
10464       for (i = 0; i < MAX_PLAYERS; i++)
10465       {
10466         if (trigger_player_bits & (1 << i))
10467         {
10468           int artwork_element = action_arg_element;
10469
10470           if (action_arg == CA_ARG_ELEMENT_RESET)
10471             artwork_element =
10472               (level.use_artwork_element[i] ? level.artwork_element[i] :
10473                stored_player[i].element_nr);
10474
10475 #if USE_GFX_RESET_PLAYER_ARTWORK
10476           if (stored_player[i].artwork_element != artwork_element)
10477             stored_player[i].Frame = 0;
10478 #endif
10479
10480           stored_player[i].artwork_element = artwork_element;
10481
10482           SetPlayerWaiting(&stored_player[i], FALSE);
10483
10484           /* set number of special actions for bored and sleeping animation */
10485           stored_player[i].num_special_action_bored =
10486             get_num_special_action(artwork_element,
10487                                    ACTION_BORING_1, ACTION_BORING_LAST);
10488           stored_player[i].num_special_action_sleeping =
10489             get_num_special_action(artwork_element,
10490                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10491         }
10492       }
10493
10494       break;
10495     }
10496
10497     /* ---------- CE actions  ---------------------------------------------- */
10498
10499     case CA_SET_CE_VALUE:
10500     {
10501 #if USE_NEW_CUSTOM_VALUE
10502       int last_ce_value = CustomValue[x][y];
10503
10504       CustomValue[x][y] = action_arg_number_new;
10505
10506       if (CustomValue[x][y] != last_ce_value)
10507       {
10508         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10509         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10510
10511         if (CustomValue[x][y] == 0)
10512         {
10513           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10514           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10515         }
10516       }
10517 #endif
10518
10519       break;
10520     }
10521
10522     case CA_SET_CE_SCORE:
10523     {
10524 #if USE_NEW_CUSTOM_VALUE
10525       int last_ce_score = ei->collect_score;
10526
10527       ei->collect_score = action_arg_number_new;
10528
10529       if (ei->collect_score != last_ce_score)
10530       {
10531         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10532         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10533
10534         if (ei->collect_score == 0)
10535         {
10536           int xx, yy;
10537
10538           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10539           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10540
10541           /*
10542             This is a very special case that seems to be a mixture between
10543             CheckElementChange() and CheckTriggeredElementChange(): while
10544             the first one only affects single elements that are triggered
10545             directly, the second one affects multiple elements in the playfield
10546             that are triggered indirectly by another element. This is a third
10547             case: Changing the CE score always affects multiple identical CEs,
10548             so every affected CE must be checked, not only the single CE for
10549             which the CE score was changed in the first place (as every instance
10550             of that CE shares the same CE score, and therefore also can change)!
10551           */
10552           SCAN_PLAYFIELD(xx, yy)
10553           {
10554             if (Feld[xx][yy] == element)
10555               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10556                                  CE_SCORE_GETS_ZERO);
10557           }
10558         }
10559       }
10560 #endif
10561
10562       break;
10563     }
10564
10565     /* ---------- engine actions  ------------------------------------------ */
10566
10567     case CA_SET_ENGINE_SCAN_MODE:
10568     {
10569       InitPlayfieldScanMode(action_arg);
10570
10571       break;
10572     }
10573
10574     default:
10575       break;
10576   }
10577 }
10578
10579 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10580 {
10581   int old_element = Feld[x][y];
10582   int new_element = GetElementFromGroupElement(element);
10583   int previous_move_direction = MovDir[x][y];
10584 #if USE_NEW_CUSTOM_VALUE
10585   int last_ce_value = CustomValue[x][y];
10586 #endif
10587   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10588   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10589   boolean add_player_onto_element = (new_element_is_player &&
10590 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10591                                      /* this breaks SnakeBite when a snake is
10592                                         halfway through a door that closes */
10593                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10594                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10595 #endif
10596                                      IS_WALKABLE(old_element));
10597
10598 #if 0
10599   /* check if element under the player changes from accessible to unaccessible
10600      (needed for special case of dropping element which then changes) */
10601   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10602       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10603   {
10604     Bang(x, y);
10605
10606     return;
10607   }
10608 #endif
10609
10610   if (!add_player_onto_element)
10611   {
10612     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10613       RemoveMovingField(x, y);
10614     else
10615       RemoveField(x, y);
10616
10617     Feld[x][y] = new_element;
10618
10619 #if !USE_GFX_RESET_GFX_ANIMATION
10620     ResetGfxAnimation(x, y);
10621     ResetRandomAnimationValue(x, y);
10622 #endif
10623
10624     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10625       MovDir[x][y] = previous_move_direction;
10626
10627 #if USE_NEW_CUSTOM_VALUE
10628     if (element_info[new_element].use_last_ce_value)
10629       CustomValue[x][y] = last_ce_value;
10630 #endif
10631
10632     InitField_WithBug1(x, y, FALSE);
10633
10634     new_element = Feld[x][y];   /* element may have changed */
10635
10636 #if USE_GFX_RESET_GFX_ANIMATION
10637     ResetGfxAnimation(x, y);
10638     ResetRandomAnimationValue(x, y);
10639 #endif
10640
10641     TEST_DrawLevelField(x, y);
10642
10643     if (GFX_CRUMBLED(new_element))
10644       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10645   }
10646
10647 #if 1
10648   /* check if element under the player changes from accessible to unaccessible
10649      (needed for special case of dropping element which then changes) */
10650   /* (must be checked after creating new element for walkable group elements) */
10651 #if USE_FIX_KILLED_BY_NON_WALKABLE
10652   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10653       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10654   {
10655     Bang(x, y);
10656
10657     return;
10658   }
10659 #else
10660   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10661       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10662   {
10663     Bang(x, y);
10664
10665     return;
10666   }
10667 #endif
10668 #endif
10669
10670   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10671   if (new_element_is_player)
10672     RelocatePlayer(x, y, new_element);
10673
10674   if (is_change)
10675     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10676
10677   TestIfBadThingTouchesPlayer(x, y);
10678   TestIfPlayerTouchesCustomElement(x, y);
10679   TestIfElementTouchesCustomElement(x, y);
10680 }
10681
10682 static void CreateField(int x, int y, int element)
10683 {
10684   CreateFieldExt(x, y, element, FALSE);
10685 }
10686
10687 static void CreateElementFromChange(int x, int y, int element)
10688 {
10689   element = GET_VALID_RUNTIME_ELEMENT(element);
10690
10691 #if USE_STOP_CHANGED_ELEMENTS
10692   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10693   {
10694     int old_element = Feld[x][y];
10695
10696     /* prevent changed element from moving in same engine frame
10697        unless both old and new element can either fall or move */
10698     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10699         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10700       Stop[x][y] = TRUE;
10701   }
10702 #endif
10703
10704   CreateFieldExt(x, y, element, TRUE);
10705 }
10706
10707 static boolean ChangeElement(int x, int y, int element, int page)
10708 {
10709   struct ElementInfo *ei = &element_info[element];
10710   struct ElementChangeInfo *change = &ei->change_page[page];
10711   int ce_value = CustomValue[x][y];
10712   int ce_score = ei->collect_score;
10713   int target_element;
10714   int old_element = Feld[x][y];
10715
10716   /* always use default change event to prevent running into a loop */
10717   if (ChangeEvent[x][y] == -1)
10718     ChangeEvent[x][y] = CE_DELAY;
10719
10720   if (ChangeEvent[x][y] == CE_DELAY)
10721   {
10722     /* reset actual trigger element, trigger player and action element */
10723     change->actual_trigger_element = EL_EMPTY;
10724     change->actual_trigger_player = EL_PLAYER_1;
10725     change->actual_trigger_player_bits = CH_PLAYER_1;
10726     change->actual_trigger_side = CH_SIDE_NONE;
10727     change->actual_trigger_ce_value = 0;
10728     change->actual_trigger_ce_score = 0;
10729   }
10730
10731   /* do not change elements more than a specified maximum number of changes */
10732   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10733     return FALSE;
10734
10735   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10736
10737   if (change->explode)
10738   {
10739     Bang(x, y);
10740
10741     return TRUE;
10742   }
10743
10744   if (change->use_target_content)
10745   {
10746     boolean complete_replace = TRUE;
10747     boolean can_replace[3][3];
10748     int xx, yy;
10749
10750     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10751     {
10752       boolean is_empty;
10753       boolean is_walkable;
10754       boolean is_diggable;
10755       boolean is_collectible;
10756       boolean is_removable;
10757       boolean is_destructible;
10758       int ex = x + xx - 1;
10759       int ey = y + yy - 1;
10760       int content_element = change->target_content.e[xx][yy];
10761       int e;
10762
10763       can_replace[xx][yy] = TRUE;
10764
10765       if (ex == x && ey == y)   /* do not check changing element itself */
10766         continue;
10767
10768       if (content_element == EL_EMPTY_SPACE)
10769       {
10770         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10771
10772         continue;
10773       }
10774
10775       if (!IN_LEV_FIELD(ex, ey))
10776       {
10777         can_replace[xx][yy] = FALSE;
10778         complete_replace = FALSE;
10779
10780         continue;
10781       }
10782
10783       e = Feld[ex][ey];
10784
10785       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10786         e = MovingOrBlocked2Element(ex, ey);
10787
10788       is_empty = (IS_FREE(ex, ey) ||
10789                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10790
10791       is_walkable     = (is_empty || IS_WALKABLE(e));
10792       is_diggable     = (is_empty || IS_DIGGABLE(e));
10793       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10794       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10795       is_removable    = (is_diggable || is_collectible);
10796
10797       can_replace[xx][yy] =
10798         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10799           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10800           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10801           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10802           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10803           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10804          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10805
10806       if (!can_replace[xx][yy])
10807         complete_replace = FALSE;
10808     }
10809
10810     if (!change->only_if_complete || complete_replace)
10811     {
10812       boolean something_has_changed = FALSE;
10813
10814       if (change->only_if_complete && change->use_random_replace &&
10815           RND(100) < change->random_percentage)
10816         return FALSE;
10817
10818       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10819       {
10820         int ex = x + xx - 1;
10821         int ey = y + yy - 1;
10822         int content_element;
10823
10824         if (can_replace[xx][yy] && (!change->use_random_replace ||
10825                                     RND(100) < change->random_percentage))
10826         {
10827           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10828             RemoveMovingField(ex, ey);
10829
10830           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10831
10832           content_element = change->target_content.e[xx][yy];
10833           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10834                                               ce_value, ce_score);
10835
10836           CreateElementFromChange(ex, ey, target_element);
10837
10838           something_has_changed = TRUE;
10839
10840           /* for symmetry reasons, freeze newly created border elements */
10841           if (ex != x || ey != y)
10842             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10843         }
10844       }
10845
10846       if (something_has_changed)
10847       {
10848         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10849         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10850       }
10851     }
10852   }
10853   else
10854   {
10855     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10856                                         ce_value, ce_score);
10857
10858     if (element == EL_DIAGONAL_GROWING ||
10859         element == EL_DIAGONAL_SHRINKING)
10860     {
10861       target_element = Store[x][y];
10862
10863       Store[x][y] = EL_EMPTY;
10864     }
10865
10866     CreateElementFromChange(x, y, target_element);
10867
10868     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10869     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10870   }
10871
10872   /* this uses direct change before indirect change */
10873   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10874
10875   return TRUE;
10876 }
10877
10878 #if USE_NEW_DELAYED_ACTION
10879
10880 static void HandleElementChange(int x, int y, int page)
10881 {
10882   int element = MovingOrBlocked2Element(x, y);
10883   struct ElementInfo *ei = &element_info[element];
10884   struct ElementChangeInfo *change = &ei->change_page[page];
10885
10886 #ifdef DEBUG
10887   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10888       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10889   {
10890     printf("\n\n");
10891     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10892            x, y, element, element_info[element].token_name);
10893     printf("HandleElementChange(): This should never happen!\n");
10894     printf("\n\n");
10895   }
10896 #endif
10897
10898   /* this can happen with classic bombs on walkable, changing elements */
10899   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10900   {
10901 #if 0
10902     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10903       ChangeDelay[x][y] = 0;
10904 #endif
10905
10906     return;
10907   }
10908
10909   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10910   {
10911     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10912
10913     if (change->can_change)
10914     {
10915 #if 1
10916       /* !!! not clear why graphic animation should be reset at all here !!! */
10917       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10918 #if USE_GFX_RESET_WHEN_NOT_MOVING
10919       /* when a custom element is about to change (for example by change delay),
10920          do not reset graphic animation when the custom element is moving */
10921       if (!IS_MOVING(x, y))
10922 #endif
10923       {
10924         ResetGfxAnimation(x, y);
10925         ResetRandomAnimationValue(x, y);
10926       }
10927 #endif
10928
10929       if (change->pre_change_function)
10930         change->pre_change_function(x, y);
10931     }
10932   }
10933
10934   ChangeDelay[x][y]--;
10935
10936   if (ChangeDelay[x][y] != 0)           /* continue element change */
10937   {
10938     if (change->can_change)
10939     {
10940       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10941
10942       if (IS_ANIMATED(graphic))
10943         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10944
10945       if (change->change_function)
10946         change->change_function(x, y);
10947     }
10948   }
10949   else                                  /* finish element change */
10950   {
10951     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10952     {
10953       page = ChangePage[x][y];
10954       ChangePage[x][y] = -1;
10955
10956       change = &ei->change_page[page];
10957     }
10958
10959     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10960     {
10961       ChangeDelay[x][y] = 1;            /* try change after next move step */
10962       ChangePage[x][y] = page;          /* remember page to use for change */
10963
10964       return;
10965     }
10966
10967     if (change->can_change)
10968     {
10969       if (ChangeElement(x, y, element, page))
10970       {
10971         if (change->post_change_function)
10972           change->post_change_function(x, y);
10973       }
10974     }
10975
10976     if (change->has_action)
10977       ExecuteCustomElementAction(x, y, element, page);
10978   }
10979 }
10980
10981 #else
10982
10983 static void HandleElementChange(int x, int y, int page)
10984 {
10985   int element = MovingOrBlocked2Element(x, y);
10986   struct ElementInfo *ei = &element_info[element];
10987   struct ElementChangeInfo *change = &ei->change_page[page];
10988
10989 #ifdef DEBUG
10990   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10991   {
10992     printf("\n\n");
10993     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10994            x, y, element, element_info[element].token_name);
10995     printf("HandleElementChange(): This should never happen!\n");
10996     printf("\n\n");
10997   }
10998 #endif
10999
11000   /* this can happen with classic bombs on walkable, changing elements */
11001   if (!CAN_CHANGE(element))
11002   {
11003 #if 0
11004     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11005       ChangeDelay[x][y] = 0;
11006 #endif
11007
11008     return;
11009   }
11010
11011   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11012   {
11013     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11014
11015     ResetGfxAnimation(x, y);
11016     ResetRandomAnimationValue(x, y);
11017
11018     if (change->pre_change_function)
11019       change->pre_change_function(x, y);
11020   }
11021
11022   ChangeDelay[x][y]--;
11023
11024   if (ChangeDelay[x][y] != 0)           /* continue element change */
11025   {
11026     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11027
11028     if (IS_ANIMATED(graphic))
11029       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11030
11031     if (change->change_function)
11032       change->change_function(x, y);
11033   }
11034   else                                  /* finish element change */
11035   {
11036     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11037     {
11038       page = ChangePage[x][y];
11039       ChangePage[x][y] = -1;
11040
11041       change = &ei->change_page[page];
11042     }
11043
11044     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11045     {
11046       ChangeDelay[x][y] = 1;            /* try change after next move step */
11047       ChangePage[x][y] = page;          /* remember page to use for change */
11048
11049       return;
11050     }
11051
11052     if (ChangeElement(x, y, element, page))
11053     {
11054       if (change->post_change_function)
11055         change->post_change_function(x, y);
11056     }
11057   }
11058 }
11059
11060 #endif
11061
11062 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11063                                               int trigger_element,
11064                                               int trigger_event,
11065                                               int trigger_player,
11066                                               int trigger_side,
11067                                               int trigger_page)
11068 {
11069   boolean change_done_any = FALSE;
11070   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11071   int i;
11072
11073   if (!(trigger_events[trigger_element][trigger_event]))
11074     return FALSE;
11075
11076 #if 0
11077   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11078          trigger_event, recursion_loop_depth, recursion_loop_detected,
11079          recursion_loop_element, EL_NAME(recursion_loop_element));
11080 #endif
11081
11082   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11083
11084   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11085   {
11086     int element = EL_CUSTOM_START + i;
11087     boolean change_done = FALSE;
11088     int p;
11089
11090     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11091         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11092       continue;
11093
11094     for (p = 0; p < element_info[element].num_change_pages; p++)
11095     {
11096       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11097
11098       if (change->can_change_or_has_action &&
11099           change->has_event[trigger_event] &&
11100           change->trigger_side & trigger_side &&
11101           change->trigger_player & trigger_player &&
11102           change->trigger_page & trigger_page_bits &&
11103           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11104       {
11105         change->actual_trigger_element = trigger_element;
11106         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11107         change->actual_trigger_player_bits = trigger_player;
11108         change->actual_trigger_side = trigger_side;
11109         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11110         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11111
11112         if ((change->can_change && !change_done) || change->has_action)
11113         {
11114           int x, y;
11115
11116           SCAN_PLAYFIELD(x, y)
11117           {
11118             if (Feld[x][y] == element)
11119             {
11120               if (change->can_change && !change_done)
11121               {
11122                 ChangeDelay[x][y] = 1;
11123                 ChangeEvent[x][y] = trigger_event;
11124
11125                 HandleElementChange(x, y, p);
11126               }
11127 #if USE_NEW_DELAYED_ACTION
11128               else if (change->has_action)
11129               {
11130                 ExecuteCustomElementAction(x, y, element, p);
11131                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11132               }
11133 #else
11134               if (change->has_action)
11135               {
11136                 ExecuteCustomElementAction(x, y, element, p);
11137                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11138               }
11139 #endif
11140             }
11141           }
11142
11143           if (change->can_change)
11144           {
11145             change_done = TRUE;
11146             change_done_any = TRUE;
11147           }
11148         }
11149       }
11150     }
11151   }
11152
11153   RECURSION_LOOP_DETECTION_END();
11154
11155   return change_done_any;
11156 }
11157
11158 static boolean CheckElementChangeExt(int x, int y,
11159                                      int element,
11160                                      int trigger_element,
11161                                      int trigger_event,
11162                                      int trigger_player,
11163                                      int trigger_side)
11164 {
11165   boolean change_done = FALSE;
11166   int p;
11167
11168   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11169       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11170     return FALSE;
11171
11172   if (Feld[x][y] == EL_BLOCKED)
11173   {
11174     Blocked2Moving(x, y, &x, &y);
11175     element = Feld[x][y];
11176   }
11177
11178 #if 0
11179   /* check if element has already changed */
11180   if (Feld[x][y] != element)
11181     return FALSE;
11182 #else
11183   /* check if element has already changed or is about to change after moving */
11184   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11185        Feld[x][y] != element) ||
11186
11187       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11188        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11189         ChangePage[x][y] != -1)))
11190     return FALSE;
11191 #endif
11192
11193 #if 0
11194   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11195          trigger_event, recursion_loop_depth, recursion_loop_detected,
11196          recursion_loop_element, EL_NAME(recursion_loop_element));
11197 #endif
11198
11199   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11200
11201   for (p = 0; p < element_info[element].num_change_pages; p++)
11202   {
11203     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11204
11205     /* check trigger element for all events where the element that is checked
11206        for changing interacts with a directly adjacent element -- this is
11207        different to element changes that affect other elements to change on the
11208        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11209     boolean check_trigger_element =
11210       (trigger_event == CE_TOUCHING_X ||
11211        trigger_event == CE_HITTING_X ||
11212        trigger_event == CE_HIT_BY_X ||
11213 #if 1
11214        /* this one was forgotten until 3.2.3 */
11215        trigger_event == CE_DIGGING_X);
11216 #endif
11217
11218     if (change->can_change_or_has_action &&
11219         change->has_event[trigger_event] &&
11220         change->trigger_side & trigger_side &&
11221         change->trigger_player & trigger_player &&
11222         (!check_trigger_element ||
11223          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11224     {
11225       change->actual_trigger_element = trigger_element;
11226       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11227       change->actual_trigger_player_bits = trigger_player;
11228       change->actual_trigger_side = trigger_side;
11229       change->actual_trigger_ce_value = CustomValue[x][y];
11230       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11231
11232       /* special case: trigger element not at (x,y) position for some events */
11233       if (check_trigger_element)
11234       {
11235         static struct
11236         {
11237           int dx, dy;
11238         } move_xy[] =
11239           {
11240             {  0,  0 },
11241             { -1,  0 },
11242             { +1,  0 },
11243             {  0,  0 },
11244             {  0, -1 },
11245             {  0,  0 }, { 0, 0 }, { 0, 0 },
11246             {  0, +1 }
11247           };
11248
11249         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11250         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11251
11252         change->actual_trigger_ce_value = CustomValue[xx][yy];
11253         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11254       }
11255
11256       if (change->can_change && !change_done)
11257       {
11258         ChangeDelay[x][y] = 1;
11259         ChangeEvent[x][y] = trigger_event;
11260
11261         HandleElementChange(x, y, p);
11262
11263         change_done = TRUE;
11264       }
11265 #if USE_NEW_DELAYED_ACTION
11266       else if (change->has_action)
11267       {
11268         ExecuteCustomElementAction(x, y, element, p);
11269         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11270       }
11271 #else
11272       if (change->has_action)
11273       {
11274         ExecuteCustomElementAction(x, y, element, p);
11275         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11276       }
11277 #endif
11278     }
11279   }
11280
11281   RECURSION_LOOP_DETECTION_END();
11282
11283   return change_done;
11284 }
11285
11286 static void PlayPlayerSound(struct PlayerInfo *player)
11287 {
11288   int jx = player->jx, jy = player->jy;
11289   int sound_element = player->artwork_element;
11290   int last_action = player->last_action_waiting;
11291   int action = player->action_waiting;
11292
11293   if (player->is_waiting)
11294   {
11295     if (action != last_action)
11296       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11297     else
11298       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11299   }
11300   else
11301   {
11302     if (action != last_action)
11303       StopSound(element_info[sound_element].sound[last_action]);
11304
11305     if (last_action == ACTION_SLEEPING)
11306       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11307   }
11308 }
11309
11310 static void PlayAllPlayersSound()
11311 {
11312   int i;
11313
11314   for (i = 0; i < MAX_PLAYERS; i++)
11315     if (stored_player[i].active)
11316       PlayPlayerSound(&stored_player[i]);
11317 }
11318
11319 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11320 {
11321   boolean last_waiting = player->is_waiting;
11322   int move_dir = player->MovDir;
11323
11324   player->dir_waiting = move_dir;
11325   player->last_action_waiting = player->action_waiting;
11326
11327   if (is_waiting)
11328   {
11329     if (!last_waiting)          /* not waiting -> waiting */
11330     {
11331       player->is_waiting = TRUE;
11332
11333       player->frame_counter_bored =
11334         FrameCounter +
11335         game.player_boring_delay_fixed +
11336         GetSimpleRandom(game.player_boring_delay_random);
11337       player->frame_counter_sleeping =
11338         FrameCounter +
11339         game.player_sleeping_delay_fixed +
11340         GetSimpleRandom(game.player_sleeping_delay_random);
11341
11342       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11343     }
11344
11345     if (game.player_sleeping_delay_fixed +
11346         game.player_sleeping_delay_random > 0 &&
11347         player->anim_delay_counter == 0 &&
11348         player->post_delay_counter == 0 &&
11349         FrameCounter >= player->frame_counter_sleeping)
11350       player->is_sleeping = TRUE;
11351     else if (game.player_boring_delay_fixed +
11352              game.player_boring_delay_random > 0 &&
11353              FrameCounter >= player->frame_counter_bored)
11354       player->is_bored = TRUE;
11355
11356     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11357                               player->is_bored ? ACTION_BORING :
11358                               ACTION_WAITING);
11359
11360     if (player->is_sleeping && player->use_murphy)
11361     {
11362       /* special case for sleeping Murphy when leaning against non-free tile */
11363
11364       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11365           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11366            !IS_MOVING(player->jx - 1, player->jy)))
11367         move_dir = MV_LEFT;
11368       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11369                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11370                 !IS_MOVING(player->jx + 1, player->jy)))
11371         move_dir = MV_RIGHT;
11372       else
11373         player->is_sleeping = FALSE;
11374
11375       player->dir_waiting = move_dir;
11376     }
11377
11378     if (player->is_sleeping)
11379     {
11380       if (player->num_special_action_sleeping > 0)
11381       {
11382         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11383         {
11384           int last_special_action = player->special_action_sleeping;
11385           int num_special_action = player->num_special_action_sleeping;
11386           int special_action =
11387             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11388              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11389              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11390              last_special_action + 1 : ACTION_SLEEPING);
11391           int special_graphic =
11392             el_act_dir2img(player->artwork_element, special_action, move_dir);
11393
11394           player->anim_delay_counter =
11395             graphic_info[special_graphic].anim_delay_fixed +
11396             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11397           player->post_delay_counter =
11398             graphic_info[special_graphic].post_delay_fixed +
11399             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11400
11401           player->special_action_sleeping = special_action;
11402         }
11403
11404         if (player->anim_delay_counter > 0)
11405         {
11406           player->action_waiting = player->special_action_sleeping;
11407           player->anim_delay_counter--;
11408         }
11409         else if (player->post_delay_counter > 0)
11410         {
11411           player->post_delay_counter--;
11412         }
11413       }
11414     }
11415     else if (player->is_bored)
11416     {
11417       if (player->num_special_action_bored > 0)
11418       {
11419         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11420         {
11421           int special_action =
11422             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11423           int special_graphic =
11424             el_act_dir2img(player->artwork_element, special_action, move_dir);
11425
11426           player->anim_delay_counter =
11427             graphic_info[special_graphic].anim_delay_fixed +
11428             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11429           player->post_delay_counter =
11430             graphic_info[special_graphic].post_delay_fixed +
11431             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11432
11433           player->special_action_bored = special_action;
11434         }
11435
11436         if (player->anim_delay_counter > 0)
11437         {
11438           player->action_waiting = player->special_action_bored;
11439           player->anim_delay_counter--;
11440         }
11441         else if (player->post_delay_counter > 0)
11442         {
11443           player->post_delay_counter--;
11444         }
11445       }
11446     }
11447   }
11448   else if (last_waiting)        /* waiting -> not waiting */
11449   {
11450     player->is_waiting = FALSE;
11451     player->is_bored = FALSE;
11452     player->is_sleeping = FALSE;
11453
11454     player->frame_counter_bored = -1;
11455     player->frame_counter_sleeping = -1;
11456
11457     player->anim_delay_counter = 0;
11458     player->post_delay_counter = 0;
11459
11460     player->dir_waiting = player->MovDir;
11461     player->action_waiting = ACTION_DEFAULT;
11462
11463     player->special_action_bored = ACTION_DEFAULT;
11464     player->special_action_sleeping = ACTION_DEFAULT;
11465   }
11466 }
11467
11468 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11469 {
11470   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11471   int left      = player_action & JOY_LEFT;
11472   int right     = player_action & JOY_RIGHT;
11473   int up        = player_action & JOY_UP;
11474   int down      = player_action & JOY_DOWN;
11475   int button1   = player_action & JOY_BUTTON_1;
11476   int button2   = player_action & JOY_BUTTON_2;
11477   int dx        = (left ? -1 : right ? 1 : 0);
11478   int dy        = (up   ? -1 : down  ? 1 : 0);
11479
11480   if (!player->active || tape.pausing)
11481     return 0;
11482
11483   if (player_action)
11484   {
11485     if (button1)
11486       snapped = SnapField(player, dx, dy);
11487     else
11488     {
11489       if (button2)
11490         dropped = DropElement(player);
11491
11492       moved = MovePlayer(player, dx, dy);
11493     }
11494
11495     if (tape.single_step && tape.recording && !tape.pausing)
11496     {
11497       if (button1 || (dropped && !moved))
11498       {
11499         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11500         SnapField(player, 0, 0);                /* stop snapping */
11501       }
11502     }
11503
11504     SetPlayerWaiting(player, FALSE);
11505
11506     return player_action;
11507   }
11508   else
11509   {
11510     /* no actions for this player (no input at player's configured device) */
11511
11512     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11513     SnapField(player, 0, 0);
11514     CheckGravityMovementWhenNotMoving(player);
11515
11516     if (player->MovPos == 0)
11517       SetPlayerWaiting(player, TRUE);
11518
11519     if (player->MovPos == 0)    /* needed for tape.playing */
11520       player->is_moving = FALSE;
11521
11522     player->is_dropping = FALSE;
11523     player->is_dropping_pressed = FALSE;
11524     player->drop_pressed_delay = 0;
11525
11526     return 0;
11527   }
11528 }
11529
11530 static void CheckLevelTime()
11531 {
11532   int i;
11533
11534   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11535   {
11536     if (level.native_em_level->lev->home == 0)  /* all players at home */
11537     {
11538       PlayerWins(local_player);
11539
11540       AllPlayersGone = TRUE;
11541
11542       level.native_em_level->lev->home = -1;
11543     }
11544
11545     if (level.native_em_level->ply[0]->alive == 0 &&
11546         level.native_em_level->ply[1]->alive == 0 &&
11547         level.native_em_level->ply[2]->alive == 0 &&
11548         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11549       AllPlayersGone = TRUE;
11550   }
11551
11552   if (TimeFrames >= FRAMES_PER_SECOND)
11553   {
11554     TimeFrames = 0;
11555     TapeTime++;
11556
11557     for (i = 0; i < MAX_PLAYERS; i++)
11558     {
11559       struct PlayerInfo *player = &stored_player[i];
11560
11561       if (SHIELD_ON(player))
11562       {
11563         player->shield_normal_time_left--;
11564
11565         if (player->shield_deadly_time_left > 0)
11566           player->shield_deadly_time_left--;
11567       }
11568     }
11569
11570     if (!local_player->LevelSolved && !level.use_step_counter)
11571     {
11572       TimePlayed++;
11573
11574       if (TimeLeft > 0)
11575       {
11576         TimeLeft--;
11577
11578         if (TimeLeft <= 10 && setup.time_limit)
11579           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11580
11581 #if 1
11582         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11583
11584         DisplayGameControlValues();
11585 #else
11586         DrawGameValue_Time(TimeLeft);
11587 #endif
11588
11589         if (!TimeLeft && setup.time_limit)
11590         {
11591           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11592             level.native_em_level->lev->killed_out_of_time = TRUE;
11593           else
11594             for (i = 0; i < MAX_PLAYERS; i++)
11595               KillPlayer(&stored_player[i]);
11596         }
11597       }
11598 #if 1
11599       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11600       {
11601         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11602
11603         DisplayGameControlValues();
11604       }
11605 #else
11606       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11607         DrawGameValue_Time(TimePlayed);
11608 #endif
11609
11610       level.native_em_level->lev->time =
11611         (level.time == 0 ? TimePlayed : TimeLeft);
11612     }
11613
11614     if (tape.recording || tape.playing)
11615       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11616   }
11617
11618 #if 1
11619   UpdateAndDisplayGameControlValues();
11620 #else
11621   UpdateGameDoorValues();
11622   DrawGameDoorValues();
11623 #endif
11624 }
11625
11626 void AdvanceFrameAndPlayerCounters(int player_nr)
11627 {
11628   int i;
11629
11630   /* advance frame counters (global frame counter and time frame counter) */
11631   FrameCounter++;
11632   TimeFrames++;
11633
11634   /* advance player counters (counters for move delay, move animation etc.) */
11635   for (i = 0; i < MAX_PLAYERS; i++)
11636   {
11637     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11638     int move_delay_value = stored_player[i].move_delay_value;
11639     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11640
11641     if (!advance_player_counters)       /* not all players may be affected */
11642       continue;
11643
11644 #if USE_NEW_PLAYER_ANIM
11645     if (move_frames == 0)       /* less than one move per game frame */
11646     {
11647       int stepsize = TILEX / move_delay_value;
11648       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11649       int count = (stored_player[i].is_moving ?
11650                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11651
11652       if (count % delay == 0)
11653         move_frames = 1;
11654     }
11655 #endif
11656
11657     stored_player[i].Frame += move_frames;
11658
11659     if (stored_player[i].MovPos != 0)
11660       stored_player[i].StepFrame += move_frames;
11661
11662     if (stored_player[i].move_delay > 0)
11663       stored_player[i].move_delay--;
11664
11665     /* due to bugs in previous versions, counter must count up, not down */
11666     if (stored_player[i].push_delay != -1)
11667       stored_player[i].push_delay++;
11668
11669     if (stored_player[i].drop_delay > 0)
11670       stored_player[i].drop_delay--;
11671
11672     if (stored_player[i].is_dropping_pressed)
11673       stored_player[i].drop_pressed_delay++;
11674   }
11675 }
11676
11677 void StartGameActions(boolean init_network_game, boolean record_tape,
11678                       long random_seed)
11679 {
11680   unsigned long new_random_seed = InitRND(random_seed);
11681
11682   if (record_tape)
11683     TapeStartRecording(new_random_seed);
11684
11685 #if defined(NETWORK_AVALIABLE)
11686   if (init_network_game)
11687   {
11688     SendToServer_StartPlaying();
11689
11690     return;
11691   }
11692 #endif
11693
11694   InitGame();
11695 }
11696
11697 void GameActions()
11698 {
11699   static unsigned long game_frame_delay = 0;
11700   unsigned long game_frame_delay_value;
11701   byte *recorded_player_action;
11702   byte summarized_player_action = 0;
11703   byte tape_action[MAX_PLAYERS];
11704   int i;
11705
11706   /* detect endless loops, caused by custom element programming */
11707   if (recursion_loop_detected && recursion_loop_depth == 0)
11708   {
11709     char *message = getStringCat3("Internal Error ! Element ",
11710                                   EL_NAME(recursion_loop_element),
11711                                   " caused endless loop ! Quit the game ?");
11712
11713     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11714           EL_NAME(recursion_loop_element));
11715
11716     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11717
11718     recursion_loop_detected = FALSE;    /* if game should be continued */
11719
11720     free(message);
11721
11722     return;
11723   }
11724
11725   if (game.restart_level)
11726     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11727
11728   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11729   {
11730     if (level.native_em_level->lev->home == 0)  /* all players at home */
11731     {
11732       PlayerWins(local_player);
11733
11734       AllPlayersGone = TRUE;
11735
11736       level.native_em_level->lev->home = -1;
11737     }
11738
11739     if (level.native_em_level->ply[0]->alive == 0 &&
11740         level.native_em_level->ply[1]->alive == 0 &&
11741         level.native_em_level->ply[2]->alive == 0 &&
11742         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11743       AllPlayersGone = TRUE;
11744   }
11745
11746   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11747     GameWon();
11748
11749   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11750     TapeStop();
11751
11752   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11753     return;
11754
11755   game_frame_delay_value =
11756     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11757
11758   if (tape.playing && tape.warp_forward && !tape.pausing)
11759     game_frame_delay_value = 0;
11760
11761   /* ---------- main game synchronization point ---------- */
11762
11763   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11764
11765   if (network_playing && !network_player_action_received)
11766   {
11767     /* try to get network player actions in time */
11768
11769 #if defined(NETWORK_AVALIABLE)
11770     /* last chance to get network player actions without main loop delay */
11771     HandleNetworking();
11772 #endif
11773
11774     /* game was quit by network peer */
11775     if (game_status != GAME_MODE_PLAYING)
11776       return;
11777
11778     if (!network_player_action_received)
11779       return;           /* failed to get network player actions in time */
11780
11781     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11782   }
11783
11784   if (tape.pausing)
11785     return;
11786
11787   /* at this point we know that we really continue executing the game */
11788
11789   network_player_action_received = FALSE;
11790
11791   /* when playing tape, read previously recorded player input from tape data */
11792   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11793
11794 #if 1
11795   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11796   if (tape.pausing)
11797     return;
11798 #endif
11799
11800   if (tape.set_centered_player)
11801   {
11802     game.centered_player_nr_next = tape.centered_player_nr_next;
11803     game.set_centered_player = TRUE;
11804   }
11805
11806   for (i = 0; i < MAX_PLAYERS; i++)
11807   {
11808     summarized_player_action |= stored_player[i].action;
11809
11810     if (!network_playing)
11811       stored_player[i].effective_action = stored_player[i].action;
11812   }
11813
11814 #if defined(NETWORK_AVALIABLE)
11815   if (network_playing)
11816     SendToServer_MovePlayer(summarized_player_action);
11817 #endif
11818
11819   if (!options.network && !setup.team_mode)
11820     local_player->effective_action = summarized_player_action;
11821
11822   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11823   {
11824     for (i = 0; i < MAX_PLAYERS; i++)
11825       stored_player[i].effective_action =
11826         (i == game.centered_player_nr ? summarized_player_action : 0);
11827   }
11828
11829   if (recorded_player_action != NULL)
11830     for (i = 0; i < MAX_PLAYERS; i++)
11831       stored_player[i].effective_action = recorded_player_action[i];
11832
11833   for (i = 0; i < MAX_PLAYERS; i++)
11834   {
11835     tape_action[i] = stored_player[i].effective_action;
11836
11837     /* (this can only happen in the R'n'D game engine) */
11838     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11839       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11840   }
11841
11842   /* only record actions from input devices, but not programmed actions */
11843   if (tape.recording)
11844     TapeRecordAction(tape_action);
11845
11846   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11847   {
11848     GameActions_EM_Main();
11849   }
11850   else
11851   {
11852     GameActions_RND();
11853   }
11854 }
11855
11856 void GameActions_EM_Main()
11857 {
11858   byte effective_action[MAX_PLAYERS];
11859   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11860   int i;
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863     effective_action[i] = stored_player[i].effective_action;
11864
11865   GameActions_EM(effective_action, warp_mode);
11866
11867   CheckLevelTime();
11868
11869   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11870 }
11871
11872 void GameActions_RND()
11873 {
11874   int magic_wall_x = 0, magic_wall_y = 0;
11875   int i, x, y, element, graphic;
11876
11877   InitPlayfieldScanModeVars();
11878
11879 #if USE_ONE_MORE_CHANGE_PER_FRAME
11880   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11881   {
11882     SCAN_PLAYFIELD(x, y)
11883     {
11884       ChangeCount[x][y] = 0;
11885       ChangeEvent[x][y] = -1;
11886     }
11887   }
11888 #endif
11889
11890   if (game.set_centered_player)
11891   {
11892     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11893
11894     /* switching to "all players" only possible if all players fit to screen */
11895     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11896     {
11897       game.centered_player_nr_next = game.centered_player_nr;
11898       game.set_centered_player = FALSE;
11899     }
11900
11901     /* do not switch focus to non-existing (or non-active) player */
11902     if (game.centered_player_nr_next >= 0 &&
11903         !stored_player[game.centered_player_nr_next].active)
11904     {
11905       game.centered_player_nr_next = game.centered_player_nr;
11906       game.set_centered_player = FALSE;
11907     }
11908   }
11909
11910   if (game.set_centered_player &&
11911       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11912   {
11913     int sx, sy;
11914
11915     if (game.centered_player_nr_next == -1)
11916     {
11917       setScreenCenteredToAllPlayers(&sx, &sy);
11918     }
11919     else
11920     {
11921       sx = stored_player[game.centered_player_nr_next].jx;
11922       sy = stored_player[game.centered_player_nr_next].jy;
11923     }
11924
11925     game.centered_player_nr = game.centered_player_nr_next;
11926     game.set_centered_player = FALSE;
11927
11928     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11929     DrawGameDoorValues();
11930   }
11931
11932   for (i = 0; i < MAX_PLAYERS; i++)
11933   {
11934     int actual_player_action = stored_player[i].effective_action;
11935
11936 #if 1
11937     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11938        - rnd_equinox_tetrachloride 048
11939        - rnd_equinox_tetrachloride_ii 096
11940        - rnd_emanuel_schmieg 002
11941        - doctor_sloan_ww 001, 020
11942     */
11943     if (stored_player[i].MovPos == 0)
11944       CheckGravityMovement(&stored_player[i]);
11945 #endif
11946
11947     /* overwrite programmed action with tape action */
11948     if (stored_player[i].programmed_action)
11949       actual_player_action = stored_player[i].programmed_action;
11950
11951     PlayerActions(&stored_player[i], actual_player_action);
11952
11953     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11954   }
11955
11956   ScrollScreen(NULL, SCROLL_GO_ON);
11957
11958   /* for backwards compatibility, the following code emulates a fixed bug that
11959      occured when pushing elements (causing elements that just made their last
11960      pushing step to already (if possible) make their first falling step in the
11961      same game frame, which is bad); this code is also needed to use the famous
11962      "spring push bug" which is used in older levels and might be wanted to be
11963      used also in newer levels, but in this case the buggy pushing code is only
11964      affecting the "spring" element and no other elements */
11965
11966   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11967   {
11968     for (i = 0; i < MAX_PLAYERS; i++)
11969     {
11970       struct PlayerInfo *player = &stored_player[i];
11971       int x = player->jx;
11972       int y = player->jy;
11973
11974       if (player->active && player->is_pushing && player->is_moving &&
11975           IS_MOVING(x, y) &&
11976           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11977            Feld[x][y] == EL_SPRING))
11978       {
11979         ContinueMoving(x, y);
11980
11981         /* continue moving after pushing (this is actually a bug) */
11982         if (!IS_MOVING(x, y))
11983           Stop[x][y] = FALSE;
11984       }
11985     }
11986   }
11987
11988 #if 0
11989   debug_print_timestamp(0, "start main loop profiling");
11990 #endif
11991
11992   SCAN_PLAYFIELD(x, y)
11993   {
11994     ChangeCount[x][y] = 0;
11995     ChangeEvent[x][y] = -1;
11996
11997     /* this must be handled before main playfield loop */
11998     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11999     {
12000       MovDelay[x][y]--;
12001       if (MovDelay[x][y] <= 0)
12002         RemoveField(x, y);
12003     }
12004
12005 #if USE_NEW_SNAP_DELAY
12006     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12007     {
12008       MovDelay[x][y]--;
12009       if (MovDelay[x][y] <= 0)
12010       {
12011         RemoveField(x, y);
12012         TEST_DrawLevelField(x, y);
12013
12014         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12015       }
12016     }
12017 #endif
12018
12019 #if DEBUG
12020     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12021     {
12022       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12023       printf("GameActions(): This should never happen!\n");
12024
12025       ChangePage[x][y] = -1;
12026     }
12027 #endif
12028
12029     Stop[x][y] = FALSE;
12030     if (WasJustMoving[x][y] > 0)
12031       WasJustMoving[x][y]--;
12032     if (WasJustFalling[x][y] > 0)
12033       WasJustFalling[x][y]--;
12034     if (CheckCollision[x][y] > 0)
12035       CheckCollision[x][y]--;
12036     if (CheckImpact[x][y] > 0)
12037       CheckImpact[x][y]--;
12038
12039     GfxFrame[x][y]++;
12040
12041     /* reset finished pushing action (not done in ContinueMoving() to allow
12042        continuous pushing animation for elements with zero push delay) */
12043     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12044     {
12045       ResetGfxAnimation(x, y);
12046       TEST_DrawLevelField(x, y);
12047     }
12048
12049 #if DEBUG
12050     if (IS_BLOCKED(x, y))
12051     {
12052       int oldx, oldy;
12053
12054       Blocked2Moving(x, y, &oldx, &oldy);
12055       if (!IS_MOVING(oldx, oldy))
12056       {
12057         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12058         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12059         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12060         printf("GameActions(): This should never happen!\n");
12061       }
12062     }
12063 #endif
12064   }
12065
12066 #if 0
12067   debug_print_timestamp(0, "- time for pre-main loop:");
12068 #endif
12069
12070 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12071   SCAN_PLAYFIELD(x, y)
12072   {
12073     element = Feld[x][y];
12074     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12075
12076 #if 1
12077     {
12078 #if 1
12079       int element2 = element;
12080       int graphic2 = graphic;
12081 #else
12082       int element2 = Feld[x][y];
12083       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12084 #endif
12085       int last_gfx_frame = GfxFrame[x][y];
12086
12087       if (graphic_info[graphic2].anim_global_sync)
12088         GfxFrame[x][y] = FrameCounter;
12089       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12090         GfxFrame[x][y] = CustomValue[x][y];
12091       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12092         GfxFrame[x][y] = element_info[element2].collect_score;
12093       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12094         GfxFrame[x][y] = ChangeDelay[x][y];
12095
12096       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12097         DrawLevelGraphicAnimation(x, y, graphic2);
12098     }
12099 #else
12100     ResetGfxFrame(x, y, TRUE);
12101 #endif
12102
12103 #if 1
12104     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12105         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12106       ResetRandomAnimationValue(x, y);
12107 #endif
12108
12109 #if 1
12110     SetRandomAnimationValue(x, y);
12111 #endif
12112
12113 #if 1
12114     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12115 #endif
12116   }
12117 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12118
12119 #if 0
12120   debug_print_timestamp(0, "- time for TEST loop:     -->");
12121 #endif
12122
12123   SCAN_PLAYFIELD(x, y)
12124   {
12125     element = Feld[x][y];
12126     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12127
12128     ResetGfxFrame(x, y, TRUE);
12129
12130     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12131         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12132       ResetRandomAnimationValue(x, y);
12133
12134     SetRandomAnimationValue(x, y);
12135
12136     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12137
12138     if (IS_INACTIVE(element))
12139     {
12140       if (IS_ANIMATED(graphic))
12141         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12142
12143       continue;
12144     }
12145
12146     /* this may take place after moving, so 'element' may have changed */
12147     if (IS_CHANGING(x, y) &&
12148         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12149     {
12150       int page = element_info[element].event_page_nr[CE_DELAY];
12151
12152 #if 1
12153       HandleElementChange(x, y, page);
12154 #else
12155       if (CAN_CHANGE(element))
12156         HandleElementChange(x, y, page);
12157
12158       if (HAS_ACTION(element))
12159         ExecuteCustomElementAction(x, y, element, page);
12160 #endif
12161
12162       element = Feld[x][y];
12163       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12164     }
12165
12166 #if 0   // ---------------------------------------------------------------------
12167
12168     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12169     {
12170       StartMoving(x, y);
12171
12172       element = Feld[x][y];
12173       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12174
12175       if (IS_ANIMATED(graphic) &&
12176           !IS_MOVING(x, y) &&
12177           !Stop[x][y])
12178         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12179
12180       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12181         TEST_DrawTwinkleOnField(x, y);
12182     }
12183     else if (IS_MOVING(x, y))
12184       ContinueMoving(x, y);
12185     else
12186     {
12187       switch (element)
12188       {
12189         case EL_ACID:
12190         case EL_EXIT_OPEN:
12191         case EL_EM_EXIT_OPEN:
12192         case EL_SP_EXIT_OPEN:
12193         case EL_STEEL_EXIT_OPEN:
12194         case EL_EM_STEEL_EXIT_OPEN:
12195         case EL_SP_TERMINAL:
12196         case EL_SP_TERMINAL_ACTIVE:
12197         case EL_EXTRA_TIME:
12198         case EL_SHIELD_NORMAL:
12199         case EL_SHIELD_DEADLY:
12200           if (IS_ANIMATED(graphic))
12201             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12202           break;
12203
12204         case EL_DYNAMITE_ACTIVE:
12205         case EL_EM_DYNAMITE_ACTIVE:
12206         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12207         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12208         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12209         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12210         case EL_SP_DISK_RED_ACTIVE:
12211           CheckDynamite(x, y);
12212           break;
12213
12214         case EL_AMOEBA_GROWING:
12215           AmoebeWaechst(x, y);
12216           break;
12217
12218         case EL_AMOEBA_SHRINKING:
12219           AmoebaDisappearing(x, y);
12220           break;
12221
12222 #if !USE_NEW_AMOEBA_CODE
12223         case EL_AMOEBA_WET:
12224         case EL_AMOEBA_DRY:
12225         case EL_AMOEBA_FULL:
12226         case EL_BD_AMOEBA:
12227         case EL_EMC_DRIPPER:
12228           AmoebeAbleger(x, y);
12229           break;
12230 #endif
12231
12232         case EL_GAME_OF_LIFE:
12233         case EL_BIOMAZE:
12234           Life(x, y);
12235           break;
12236
12237         case EL_EXIT_CLOSED:
12238           CheckExit(x, y);
12239           break;
12240
12241         case EL_EM_EXIT_CLOSED:
12242           CheckExitEM(x, y);
12243           break;
12244
12245         case EL_STEEL_EXIT_CLOSED:
12246           CheckExitSteel(x, y);
12247           break;
12248
12249         case EL_EM_STEEL_EXIT_CLOSED:
12250           CheckExitSteelEM(x, y);
12251           break;
12252
12253         case EL_SP_EXIT_CLOSED:
12254           CheckExitSP(x, y);
12255           break;
12256
12257         case EL_EXPANDABLE_WALL_GROWING:
12258         case EL_EXPANDABLE_STEELWALL_GROWING:
12259           MauerWaechst(x, y);
12260           break;
12261
12262         case EL_EXPANDABLE_WALL:
12263         case EL_EXPANDABLE_WALL_HORIZONTAL:
12264         case EL_EXPANDABLE_WALL_VERTICAL:
12265         case EL_EXPANDABLE_WALL_ANY:
12266         case EL_BD_EXPANDABLE_WALL:
12267           MauerAbleger(x, y);
12268           break;
12269
12270         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12271         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12272         case EL_EXPANDABLE_STEELWALL_ANY:
12273           MauerAblegerStahl(x, y);
12274           break;
12275
12276         case EL_FLAMES:
12277           CheckForDragon(x, y);
12278           break;
12279
12280         case EL_EXPLOSION:
12281           break;
12282
12283         case EL_ELEMENT_SNAPPING:
12284         case EL_DIAGONAL_SHRINKING:
12285         case EL_DIAGONAL_GROWING:
12286         {
12287           graphic =
12288             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12289
12290           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12291           break;
12292         }
12293
12294         default:
12295           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12296             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12297           break;
12298       }
12299     }
12300
12301 #else   // ---------------------------------------------------------------------
12302
12303     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12304     {
12305       StartMoving(x, y);
12306
12307       element = Feld[x][y];
12308       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12309
12310       if (IS_ANIMATED(graphic) &&
12311           !IS_MOVING(x, y) &&
12312           !Stop[x][y])
12313         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12314
12315       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12316         TEST_DrawTwinkleOnField(x, y);
12317     }
12318     else if ((element == EL_ACID ||
12319               element == EL_EXIT_OPEN ||
12320               element == EL_EM_EXIT_OPEN ||
12321               element == EL_SP_EXIT_OPEN ||
12322               element == EL_STEEL_EXIT_OPEN ||
12323               element == EL_EM_STEEL_EXIT_OPEN ||
12324               element == EL_SP_TERMINAL ||
12325               element == EL_SP_TERMINAL_ACTIVE ||
12326               element == EL_EXTRA_TIME ||
12327               element == EL_SHIELD_NORMAL ||
12328               element == EL_SHIELD_DEADLY) &&
12329              IS_ANIMATED(graphic))
12330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12331     else if (IS_MOVING(x, y))
12332       ContinueMoving(x, y);
12333     else if (IS_ACTIVE_BOMB(element))
12334       CheckDynamite(x, y);
12335     else if (element == EL_AMOEBA_GROWING)
12336       AmoebeWaechst(x, y);
12337     else if (element == EL_AMOEBA_SHRINKING)
12338       AmoebaDisappearing(x, y);
12339
12340 #if !USE_NEW_AMOEBA_CODE
12341     else if (IS_AMOEBALIVE(element))
12342       AmoebeAbleger(x, y);
12343 #endif
12344
12345     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12346       Life(x, y);
12347     else if (element == EL_EXIT_CLOSED)
12348       CheckExit(x, y);
12349     else if (element == EL_EM_EXIT_CLOSED)
12350       CheckExitEM(x, y);
12351     else if (element == EL_STEEL_EXIT_CLOSED)
12352       CheckExitSteel(x, y);
12353     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12354       CheckExitSteelEM(x, y);
12355     else if (element == EL_SP_EXIT_CLOSED)
12356       CheckExitSP(x, y);
12357     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12358              element == EL_EXPANDABLE_STEELWALL_GROWING)
12359       MauerWaechst(x, y);
12360     else if (element == EL_EXPANDABLE_WALL ||
12361              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12362              element == EL_EXPANDABLE_WALL_VERTICAL ||
12363              element == EL_EXPANDABLE_WALL_ANY ||
12364              element == EL_BD_EXPANDABLE_WALL)
12365       MauerAbleger(x, y);
12366     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12367              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12368              element == EL_EXPANDABLE_STEELWALL_ANY)
12369       MauerAblegerStahl(x, y);
12370     else if (element == EL_FLAMES)
12371       CheckForDragon(x, y);
12372     else if (element == EL_EXPLOSION)
12373       ; /* drawing of correct explosion animation is handled separately */
12374     else if (element == EL_ELEMENT_SNAPPING ||
12375              element == EL_DIAGONAL_SHRINKING ||
12376              element == EL_DIAGONAL_GROWING)
12377     {
12378       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12379
12380       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12381     }
12382     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12383       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12384
12385 #endif  // ---------------------------------------------------------------------
12386
12387     if (IS_BELT_ACTIVE(element))
12388       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12389
12390     if (game.magic_wall_active)
12391     {
12392       int jx = local_player->jx, jy = local_player->jy;
12393
12394       /* play the element sound at the position nearest to the player */
12395       if ((element == EL_MAGIC_WALL_FULL ||
12396            element == EL_MAGIC_WALL_ACTIVE ||
12397            element == EL_MAGIC_WALL_EMPTYING ||
12398            element == EL_BD_MAGIC_WALL_FULL ||
12399            element == EL_BD_MAGIC_WALL_ACTIVE ||
12400            element == EL_BD_MAGIC_WALL_EMPTYING ||
12401            element == EL_DC_MAGIC_WALL_FULL ||
12402            element == EL_DC_MAGIC_WALL_ACTIVE ||
12403            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12404           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12405       {
12406         magic_wall_x = x;
12407         magic_wall_y = y;
12408       }
12409     }
12410   }
12411
12412 #if 0
12413   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12414 #endif
12415
12416 #if USE_NEW_AMOEBA_CODE
12417   /* new experimental amoeba growth stuff */
12418   if (!(FrameCounter % 8))
12419   {
12420     static unsigned long random = 1684108901;
12421
12422     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12423     {
12424       x = RND(lev_fieldx);
12425       y = RND(lev_fieldy);
12426       element = Feld[x][y];
12427
12428       if (!IS_PLAYER(x,y) &&
12429           (element == EL_EMPTY ||
12430            CAN_GROW_INTO(element) ||
12431            element == EL_QUICKSAND_EMPTY ||
12432            element == EL_QUICKSAND_FAST_EMPTY ||
12433            element == EL_ACID_SPLASH_LEFT ||
12434            element == EL_ACID_SPLASH_RIGHT))
12435       {
12436         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12437             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12438             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12439             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12440           Feld[x][y] = EL_AMOEBA_DROP;
12441       }
12442
12443       random = random * 129 + 1;
12444     }
12445   }
12446 #endif
12447
12448 #if 0
12449   if (game.explosions_delayed)
12450 #endif
12451   {
12452     game.explosions_delayed = FALSE;
12453
12454     SCAN_PLAYFIELD(x, y)
12455     {
12456       element = Feld[x][y];
12457
12458       if (ExplodeField[x][y])
12459         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12460       else if (element == EL_EXPLOSION)
12461         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12462
12463       ExplodeField[x][y] = EX_TYPE_NONE;
12464     }
12465
12466     game.explosions_delayed = TRUE;
12467   }
12468
12469   if (game.magic_wall_active)
12470   {
12471     if (!(game.magic_wall_time_left % 4))
12472     {
12473       int element = Feld[magic_wall_x][magic_wall_y];
12474
12475       if (element == EL_BD_MAGIC_WALL_FULL ||
12476           element == EL_BD_MAGIC_WALL_ACTIVE ||
12477           element == EL_BD_MAGIC_WALL_EMPTYING)
12478         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12479       else if (element == EL_DC_MAGIC_WALL_FULL ||
12480                element == EL_DC_MAGIC_WALL_ACTIVE ||
12481                element == EL_DC_MAGIC_WALL_EMPTYING)
12482         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12483       else
12484         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12485     }
12486
12487     if (game.magic_wall_time_left > 0)
12488     {
12489       game.magic_wall_time_left--;
12490
12491       if (!game.magic_wall_time_left)
12492       {
12493         SCAN_PLAYFIELD(x, y)
12494         {
12495           element = Feld[x][y];
12496
12497           if (element == EL_MAGIC_WALL_ACTIVE ||
12498               element == EL_MAGIC_WALL_FULL)
12499           {
12500             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12501             TEST_DrawLevelField(x, y);
12502           }
12503           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12504                    element == EL_BD_MAGIC_WALL_FULL)
12505           {
12506             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12507             TEST_DrawLevelField(x, y);
12508           }
12509           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12510                    element == EL_DC_MAGIC_WALL_FULL)
12511           {
12512             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12513             TEST_DrawLevelField(x, y);
12514           }
12515         }
12516
12517         game.magic_wall_active = FALSE;
12518       }
12519     }
12520   }
12521
12522   if (game.light_time_left > 0)
12523   {
12524     game.light_time_left--;
12525
12526     if (game.light_time_left == 0)
12527       RedrawAllLightSwitchesAndInvisibleElements();
12528   }
12529
12530   if (game.timegate_time_left > 0)
12531   {
12532     game.timegate_time_left--;
12533
12534     if (game.timegate_time_left == 0)
12535       CloseAllOpenTimegates();
12536   }
12537
12538   if (game.lenses_time_left > 0)
12539   {
12540     game.lenses_time_left--;
12541
12542     if (game.lenses_time_left == 0)
12543       RedrawAllInvisibleElementsForLenses();
12544   }
12545
12546   if (game.magnify_time_left > 0)
12547   {
12548     game.magnify_time_left--;
12549
12550     if (game.magnify_time_left == 0)
12551       RedrawAllInvisibleElementsForMagnifier();
12552   }
12553
12554   for (i = 0; i < MAX_PLAYERS; i++)
12555   {
12556     struct PlayerInfo *player = &stored_player[i];
12557
12558     if (SHIELD_ON(player))
12559     {
12560       if (player->shield_deadly_time_left)
12561         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12562       else if (player->shield_normal_time_left)
12563         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12564     }
12565   }
12566
12567 #if USE_DELAYED_GFX_REDRAW
12568   SCAN_PLAYFIELD(x, y)
12569   {
12570 #if 1
12571     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12572 #else
12573     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12574         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12575 #endif
12576     {
12577       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12578          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12579
12580       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12581         DrawLevelField(x, y);
12582
12583       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12584         DrawLevelFieldCrumbledSand(x, y);
12585
12586       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12587         DrawLevelFieldCrumbledSandNeighbours(x, y);
12588
12589       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12590         DrawTwinkleOnField(x, y);
12591     }
12592
12593     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12594   }
12595 #endif
12596
12597   CheckLevelTime();
12598
12599   DrawAllPlayers();
12600   PlayAllPlayersSound();
12601
12602   if (options.debug)                    /* calculate frames per second */
12603   {
12604     static unsigned long fps_counter = 0;
12605     static int fps_frames = 0;
12606     unsigned long fps_delay_ms = Counter() - fps_counter;
12607
12608     fps_frames++;
12609
12610     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12611     {
12612       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12613
12614       fps_frames = 0;
12615       fps_counter = Counter();
12616     }
12617
12618     redraw_mask |= REDRAW_FPS;
12619   }
12620
12621   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12622
12623   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12624   {
12625     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12626
12627     local_player->show_envelope = 0;
12628   }
12629
12630 #if 0
12631   debug_print_timestamp(0, "stop main loop profiling ");
12632   printf("----------------------------------------------------------\n");
12633 #endif
12634
12635   /* use random number generator in every frame to make it less predictable */
12636   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12637     RND(1);
12638 }
12639
12640 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12641 {
12642   int min_x = x, min_y = y, max_x = x, max_y = y;
12643   int i;
12644
12645   for (i = 0; i < MAX_PLAYERS; i++)
12646   {
12647     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12648
12649     if (!stored_player[i].active || &stored_player[i] == player)
12650       continue;
12651
12652     min_x = MIN(min_x, jx);
12653     min_y = MIN(min_y, jy);
12654     max_x = MAX(max_x, jx);
12655     max_y = MAX(max_y, jy);
12656   }
12657
12658   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12659 }
12660
12661 static boolean AllPlayersInVisibleScreen()
12662 {
12663   int i;
12664
12665   for (i = 0; i < MAX_PLAYERS; i++)
12666   {
12667     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12668
12669     if (!stored_player[i].active)
12670       continue;
12671
12672     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12673       return FALSE;
12674   }
12675
12676   return TRUE;
12677 }
12678
12679 void ScrollLevel(int dx, int dy)
12680 {
12681 #if 0
12682   /* (directly solved in BlitBitmap() now) */
12683   static Bitmap *bitmap_db_field2 = NULL;
12684   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12685   int x, y;
12686 #else
12687   int i, x, y;
12688 #endif
12689
12690 #if 0
12691   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12692   /* only horizontal XOR vertical scroll direction allowed */
12693   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12694     return;
12695 #endif
12696
12697 #if 0
12698   /* (directly solved in BlitBitmap() now) */
12699   if (bitmap_db_field2 == NULL)
12700     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12701
12702   /* needed when blitting directly to same bitmap -- should not be needed with
12703      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12704   BlitBitmap(drawto_field, bitmap_db_field2,
12705              FX + TILEX * (dx == -1) - softscroll_offset,
12706              FY + TILEY * (dy == -1) - softscroll_offset,
12707              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12708              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12709              FX + TILEX * (dx == 1) - softscroll_offset,
12710              FY + TILEY * (dy == 1) - softscroll_offset);
12711   BlitBitmap(bitmap_db_field2, drawto_field,
12712              FX + TILEX * (dx == 1) - softscroll_offset,
12713              FY + TILEY * (dy == 1) - softscroll_offset,
12714              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12715              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12716              FX + TILEX * (dx == 1) - softscroll_offset,
12717              FY + TILEY * (dy == 1) - softscroll_offset);
12718
12719 #else
12720
12721 #if 0
12722   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12723   int xsize = (BX2 - BX1 + 1);
12724   int ysize = (BY2 - BY1 + 1);
12725   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12726   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12727   int step  = (start < end ? +1 : -1);
12728
12729   for (i = start; i != end; i += step)
12730   {
12731     BlitBitmap(drawto_field, drawto_field,
12732                FX + TILEX * (dx != 0 ? i + step : 0),
12733                FY + TILEY * (dy != 0 ? i + step : 0),
12734                TILEX * (dx != 0 ? 1 : xsize),
12735                TILEY * (dy != 0 ? 1 : ysize),
12736                FX + TILEX * (dx != 0 ? i : 0),
12737                FY + TILEY * (dy != 0 ? i : 0));
12738   }
12739
12740 #else
12741
12742   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12743
12744   BlitBitmap(drawto_field, drawto_field,
12745              FX + TILEX * (dx == -1) - softscroll_offset,
12746              FY + TILEY * (dy == -1) - softscroll_offset,
12747              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12748              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12749              FX + TILEX * (dx == 1) - softscroll_offset,
12750              FY + TILEY * (dy == 1) - softscroll_offset);
12751 #endif
12752 #endif
12753
12754   if (dx != 0)
12755   {
12756     x = (dx == 1 ? BX1 : BX2);
12757     for (y = BY1; y <= BY2; y++)
12758       DrawScreenField(x, y);
12759   }
12760
12761   if (dy != 0)
12762   {
12763     y = (dy == 1 ? BY1 : BY2);
12764     for (x = BX1; x <= BX2; x++)
12765       DrawScreenField(x, y);
12766   }
12767
12768   redraw_mask |= REDRAW_FIELD;
12769 }
12770
12771 static boolean canFallDown(struct PlayerInfo *player)
12772 {
12773   int jx = player->jx, jy = player->jy;
12774
12775   return (IN_LEV_FIELD(jx, jy + 1) &&
12776           (IS_FREE(jx, jy + 1) ||
12777            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12778           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12779           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12780 }
12781
12782 static boolean canPassField(int x, int y, int move_dir)
12783 {
12784   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12785   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12786   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12787   int nextx = x + dx;
12788   int nexty = y + dy;
12789   int element = Feld[x][y];
12790
12791   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12792           !CAN_MOVE(element) &&
12793           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12794           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12795           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12796 }
12797
12798 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12799 {
12800   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12801   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12802   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12803   int newx = x + dx;
12804   int newy = y + dy;
12805
12806   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12807           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12808           (IS_DIGGABLE(Feld[newx][newy]) ||
12809            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12810            canPassField(newx, newy, move_dir)));
12811 }
12812
12813 static void CheckGravityMovement(struct PlayerInfo *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 move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12822     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12823     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12824     int jx = player->jx, jy = player->jy;
12825     boolean player_is_moving_to_valid_field =
12826       (!player_is_snapping &&
12827        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12828         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12829     boolean player_can_fall_down = canFallDown(player);
12830
12831     if (player_can_fall_down &&
12832         !player_is_moving_to_valid_field)
12833       player->programmed_action = MV_DOWN;
12834   }
12835 }
12836
12837 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12838 {
12839   return CheckGravityMovement(player);
12840
12841 #if USE_PLAYER_GRAVITY
12842   if (player->gravity && !player->programmed_action)
12843 #else
12844   if (game.gravity && !player->programmed_action)
12845 #endif
12846   {
12847     int jx = player->jx, jy = player->jy;
12848     boolean field_under_player_is_free =
12849       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12850     boolean player_is_standing_on_valid_field =
12851       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12852        (IS_WALKABLE(Feld[jx][jy]) &&
12853         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12854
12855     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12856       player->programmed_action = MV_DOWN;
12857   }
12858 }
12859
12860 /*
12861   MovePlayerOneStep()
12862   -----------------------------------------------------------------------------
12863   dx, dy:               direction (non-diagonal) to try to move the player to
12864   real_dx, real_dy:     direction as read from input device (can be diagonal)
12865 */
12866
12867 boolean MovePlayerOneStep(struct PlayerInfo *player,
12868                           int dx, int dy, int real_dx, int real_dy)
12869 {
12870   int jx = player->jx, jy = player->jy;
12871   int new_jx = jx + dx, new_jy = jy + dy;
12872 #if !USE_FIXED_DONT_RUN_INTO
12873   int element;
12874 #endif
12875   int can_move;
12876   boolean player_can_move = !player->cannot_move;
12877
12878   if (!player->active || (!dx && !dy))
12879     return MP_NO_ACTION;
12880
12881   player->MovDir = (dx < 0 ? MV_LEFT :
12882                     dx > 0 ? MV_RIGHT :
12883                     dy < 0 ? MV_UP :
12884                     dy > 0 ? MV_DOWN :  MV_NONE);
12885
12886   if (!IN_LEV_FIELD(new_jx, new_jy))
12887     return MP_NO_ACTION;
12888
12889   if (!player_can_move)
12890   {
12891     if (player->MovPos == 0)
12892     {
12893       player->is_moving = FALSE;
12894       player->is_digging = FALSE;
12895       player->is_collecting = FALSE;
12896       player->is_snapping = FALSE;
12897       player->is_pushing = FALSE;
12898     }
12899   }
12900
12901 #if 1
12902   if (!options.network && game.centered_player_nr == -1 &&
12903       !AllPlayersInSight(player, new_jx, new_jy))
12904     return MP_NO_ACTION;
12905 #else
12906   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12907     return MP_NO_ACTION;
12908 #endif
12909
12910 #if !USE_FIXED_DONT_RUN_INTO
12911   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12912
12913   /* (moved to DigField()) */
12914   if (player_can_move && DONT_RUN_INTO(element))
12915   {
12916     if (element == EL_ACID && dx == 0 && dy == 1)
12917     {
12918       SplashAcid(new_jx, new_jy);
12919       Feld[jx][jy] = EL_PLAYER_1;
12920       InitMovingField(jx, jy, MV_DOWN);
12921       Store[jx][jy] = EL_ACID;
12922       ContinueMoving(jx, jy);
12923       BuryPlayer(player);
12924     }
12925     else
12926       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12927
12928     return MP_MOVING;
12929   }
12930 #endif
12931
12932   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12933   if (can_move != MP_MOVING)
12934     return can_move;
12935
12936   /* check if DigField() has caused relocation of the player */
12937   if (player->jx != jx || player->jy != jy)
12938     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12939
12940   StorePlayer[jx][jy] = 0;
12941   player->last_jx = jx;
12942   player->last_jy = jy;
12943   player->jx = new_jx;
12944   player->jy = new_jy;
12945   StorePlayer[new_jx][new_jy] = player->element_nr;
12946
12947   if (player->move_delay_value_next != -1)
12948   {
12949     player->move_delay_value = player->move_delay_value_next;
12950     player->move_delay_value_next = -1;
12951   }
12952
12953   player->MovPos =
12954     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12955
12956   player->step_counter++;
12957
12958   PlayerVisit[jx][jy] = FrameCounter;
12959
12960 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12961   player->is_moving = TRUE;
12962 #endif
12963
12964 #if 1
12965   /* should better be called in MovePlayer(), but this breaks some tapes */
12966   ScrollPlayer(player, SCROLL_INIT);
12967 #endif
12968
12969   return MP_MOVING;
12970 }
12971
12972 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12973 {
12974   int jx = player->jx, jy = player->jy;
12975   int old_jx = jx, old_jy = jy;
12976   int moved = MP_NO_ACTION;
12977
12978   if (!player->active)
12979     return FALSE;
12980
12981   if (!dx && !dy)
12982   {
12983     if (player->MovPos == 0)
12984     {
12985       player->is_moving = FALSE;
12986       player->is_digging = FALSE;
12987       player->is_collecting = FALSE;
12988       player->is_snapping = FALSE;
12989       player->is_pushing = FALSE;
12990     }
12991
12992     return FALSE;
12993   }
12994
12995   if (player->move_delay > 0)
12996     return FALSE;
12997
12998   player->move_delay = -1;              /* set to "uninitialized" value */
12999
13000   /* store if player is automatically moved to next field */
13001   player->is_auto_moving = (player->programmed_action != MV_NONE);
13002
13003   /* remove the last programmed player action */
13004   player->programmed_action = 0;
13005
13006   if (player->MovPos)
13007   {
13008     /* should only happen if pre-1.2 tape recordings are played */
13009     /* this is only for backward compatibility */
13010
13011     int original_move_delay_value = player->move_delay_value;
13012
13013 #if DEBUG
13014     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13015            tape.counter);
13016 #endif
13017
13018     /* scroll remaining steps with finest movement resolution */
13019     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13020
13021     while (player->MovPos)
13022     {
13023       ScrollPlayer(player, SCROLL_GO_ON);
13024       ScrollScreen(NULL, SCROLL_GO_ON);
13025
13026       AdvanceFrameAndPlayerCounters(player->index_nr);
13027
13028       DrawAllPlayers();
13029       BackToFront();
13030     }
13031
13032     player->move_delay_value = original_move_delay_value;
13033   }
13034
13035   player->is_active = FALSE;
13036
13037   if (player->last_move_dir & MV_HORIZONTAL)
13038   {
13039     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13040       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13041   }
13042   else
13043   {
13044     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13045       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13046   }
13047
13048 #if USE_FIXED_BORDER_RUNNING_GFX
13049   if (!moved && !player->is_active)
13050   {
13051     player->is_moving = FALSE;
13052     player->is_digging = FALSE;
13053     player->is_collecting = FALSE;
13054     player->is_snapping = FALSE;
13055     player->is_pushing = FALSE;
13056   }
13057 #endif
13058
13059   jx = player->jx;
13060   jy = player->jy;
13061
13062 #if 1
13063   if (moved & MP_MOVING && !ScreenMovPos &&
13064       (player->index_nr == game.centered_player_nr ||
13065        game.centered_player_nr == -1))
13066 #else
13067   if (moved & MP_MOVING && !ScreenMovPos &&
13068       (player == local_player || !options.network))
13069 #endif
13070   {
13071     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13072     int offset = game.scroll_delay_value;
13073
13074     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13075     {
13076       /* actual player has left the screen -- scroll in that direction */
13077       if (jx != old_jx)         /* player has moved horizontally */
13078         scroll_x += (jx - old_jx);
13079       else                      /* player has moved vertically */
13080         scroll_y += (jy - old_jy);
13081     }
13082     else
13083     {
13084       if (jx != old_jx)         /* player has moved horizontally */
13085       {
13086         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13087             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13088           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13089
13090         /* don't scroll over playfield boundaries */
13091         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13092           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13093
13094         /* don't scroll more than one field at a time */
13095         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13096
13097         /* don't scroll against the player's moving direction */
13098         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13099             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13100           scroll_x = old_scroll_x;
13101       }
13102       else                      /* player has moved vertically */
13103       {
13104         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13105             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13106           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13107
13108         /* don't scroll over playfield boundaries */
13109         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13110           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13111
13112         /* don't scroll more than one field at a time */
13113         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13114
13115         /* don't scroll against the player's moving direction */
13116         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13117             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13118           scroll_y = old_scroll_y;
13119       }
13120     }
13121
13122     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13123     {
13124 #if 1
13125       if (!options.network && game.centered_player_nr == -1 &&
13126           !AllPlayersInVisibleScreen())
13127       {
13128         scroll_x = old_scroll_x;
13129         scroll_y = old_scroll_y;
13130       }
13131       else
13132 #else
13133       if (!options.network && !AllPlayersInVisibleScreen())
13134       {
13135         scroll_x = old_scroll_x;
13136         scroll_y = old_scroll_y;
13137       }
13138       else
13139 #endif
13140       {
13141         ScrollScreen(player, SCROLL_INIT);
13142         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13143       }
13144     }
13145   }
13146
13147   player->StepFrame = 0;
13148
13149   if (moved & MP_MOVING)
13150   {
13151     if (old_jx != jx && old_jy == jy)
13152       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13153     else if (old_jx == jx && old_jy != jy)
13154       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13155
13156     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13157
13158     player->last_move_dir = player->MovDir;
13159     player->is_moving = TRUE;
13160     player->is_snapping = FALSE;
13161     player->is_switching = FALSE;
13162     player->is_dropping = FALSE;
13163     player->is_dropping_pressed = FALSE;
13164     player->drop_pressed_delay = 0;
13165
13166 #if 0
13167     /* should better be called here than above, but this breaks some tapes */
13168     ScrollPlayer(player, SCROLL_INIT);
13169 #endif
13170   }
13171   else
13172   {
13173     CheckGravityMovementWhenNotMoving(player);
13174
13175     player->is_moving = FALSE;
13176
13177     /* at this point, the player is allowed to move, but cannot move right now
13178        (e.g. because of something blocking the way) -- ensure that the player
13179        is also allowed to move in the next frame (in old versions before 3.1.1,
13180        the player was forced to wait again for eight frames before next try) */
13181
13182     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13183       player->move_delay = 0;   /* allow direct movement in the next frame */
13184   }
13185
13186   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13187     player->move_delay = player->move_delay_value;
13188
13189   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13190   {
13191     TestIfPlayerTouchesBadThing(jx, jy);
13192     TestIfPlayerTouchesCustomElement(jx, jy);
13193   }
13194
13195   if (!player->active)
13196     RemovePlayer(player);
13197
13198   return moved;
13199 }
13200
13201 void ScrollPlayer(struct PlayerInfo *player, int mode)
13202 {
13203   int jx = player->jx, jy = player->jy;
13204   int last_jx = player->last_jx, last_jy = player->last_jy;
13205   int move_stepsize = TILEX / player->move_delay_value;
13206
13207 #if USE_NEW_PLAYER_SPEED
13208   if (!player->active)
13209     return;
13210
13211   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13212     return;
13213 #else
13214   if (!player->active || player->MovPos == 0)
13215     return;
13216 #endif
13217
13218   if (mode == SCROLL_INIT)
13219   {
13220     player->actual_frame_counter = FrameCounter;
13221     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13222
13223     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13224         Feld[last_jx][last_jy] == EL_EMPTY)
13225     {
13226       int last_field_block_delay = 0;   /* start with no blocking at all */
13227       int block_delay_adjustment = player->block_delay_adjustment;
13228
13229       /* if player blocks last field, add delay for exactly one move */
13230       if (player->block_last_field)
13231       {
13232         last_field_block_delay += player->move_delay_value;
13233
13234         /* when blocking enabled, prevent moving up despite gravity */
13235 #if USE_PLAYER_GRAVITY
13236         if (player->gravity && player->MovDir == MV_UP)
13237           block_delay_adjustment = -1;
13238 #else
13239         if (game.gravity && player->MovDir == MV_UP)
13240           block_delay_adjustment = -1;
13241 #endif
13242       }
13243
13244       /* add block delay adjustment (also possible when not blocking) */
13245       last_field_block_delay += block_delay_adjustment;
13246
13247       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13248       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13249     }
13250
13251 #if USE_NEW_PLAYER_SPEED
13252     if (player->MovPos != 0)    /* player has not yet reached destination */
13253       return;
13254 #else
13255     return;
13256 #endif
13257   }
13258   else if (!FrameReached(&player->actual_frame_counter, 1))
13259     return;
13260
13261 #if USE_NEW_PLAYER_SPEED
13262   if (player->MovPos != 0)
13263   {
13264     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13265     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13266
13267     /* before DrawPlayer() to draw correct player graphic for this case */
13268     if (player->MovPos == 0)
13269       CheckGravityMovement(player);
13270   }
13271 #else
13272   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13273   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13274
13275   /* before DrawPlayer() to draw correct player graphic for this case */
13276   if (player->MovPos == 0)
13277     CheckGravityMovement(player);
13278 #endif
13279
13280   if (player->MovPos == 0)      /* player reached destination field */
13281   {
13282     if (player->move_delay_reset_counter > 0)
13283     {
13284       player->move_delay_reset_counter--;
13285
13286       if (player->move_delay_reset_counter == 0)
13287       {
13288         /* continue with normal speed after quickly moving through gate */
13289         HALVE_PLAYER_SPEED(player);
13290
13291         /* be able to make the next move without delay */
13292         player->move_delay = 0;
13293       }
13294     }
13295
13296     player->last_jx = jx;
13297     player->last_jy = jy;
13298
13299     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13300         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13301         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13302         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13303         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13304         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13305     {
13306       DrawPlayer(player);       /* needed here only to cleanup last field */
13307       RemovePlayer(player);
13308
13309       if (local_player->friends_still_needed == 0 ||
13310           IS_SP_ELEMENT(Feld[jx][jy]))
13311         PlayerWins(player);
13312     }
13313
13314     /* this breaks one level: "machine", level 000 */
13315     {
13316       int move_direction = player->MovDir;
13317       int enter_side = MV_DIR_OPPOSITE(move_direction);
13318       int leave_side = move_direction;
13319       int old_jx = last_jx;
13320       int old_jy = last_jy;
13321       int old_element = Feld[old_jx][old_jy];
13322       int new_element = Feld[jx][jy];
13323
13324       if (IS_CUSTOM_ELEMENT(old_element))
13325         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13326                                    CE_LEFT_BY_PLAYER,
13327                                    player->index_bit, leave_side);
13328
13329       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13330                                           CE_PLAYER_LEAVES_X,
13331                                           player->index_bit, leave_side);
13332
13333       if (IS_CUSTOM_ELEMENT(new_element))
13334         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13335                                    player->index_bit, enter_side);
13336
13337       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13338                                           CE_PLAYER_ENTERS_X,
13339                                           player->index_bit, enter_side);
13340
13341 #if USE_FIX_CE_ACTION_WITH_PLAYER
13342       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13343                                         CE_MOVE_OF_X, move_direction);
13344 #else
13345       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13346                                         CE_MOVE_OF_X, move_direction);
13347 #endif
13348     }
13349
13350     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13351     {
13352       TestIfPlayerTouchesBadThing(jx, jy);
13353       TestIfPlayerTouchesCustomElement(jx, jy);
13354
13355       /* needed because pushed element has not yet reached its destination,
13356          so it would trigger a change event at its previous field location */
13357       if (!player->is_pushing)
13358         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13359
13360       if (!player->active)
13361         RemovePlayer(player);
13362     }
13363
13364     if (!local_player->LevelSolved && level.use_step_counter)
13365     {
13366       int i;
13367
13368       TimePlayed++;
13369
13370       if (TimeLeft > 0)
13371       {
13372         TimeLeft--;
13373
13374         if (TimeLeft <= 10 && setup.time_limit)
13375           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13376
13377 #if 1
13378         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13379
13380         DisplayGameControlValues();
13381 #else
13382         DrawGameValue_Time(TimeLeft);
13383 #endif
13384
13385         if (!TimeLeft && setup.time_limit)
13386           for (i = 0; i < MAX_PLAYERS; i++)
13387             KillPlayer(&stored_player[i]);
13388       }
13389 #if 1
13390       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13391       {
13392         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13393
13394         DisplayGameControlValues();
13395       }
13396 #else
13397       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13398         DrawGameValue_Time(TimePlayed);
13399 #endif
13400     }
13401
13402     if (tape.single_step && tape.recording && !tape.pausing &&
13403         !player->programmed_action)
13404       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13405   }
13406 }
13407
13408 void ScrollScreen(struct PlayerInfo *player, int mode)
13409 {
13410   static unsigned long screen_frame_counter = 0;
13411
13412   if (mode == SCROLL_INIT)
13413   {
13414     /* set scrolling step size according to actual player's moving speed */
13415     ScrollStepSize = TILEX / player->move_delay_value;
13416
13417     screen_frame_counter = FrameCounter;
13418     ScreenMovDir = player->MovDir;
13419     ScreenMovPos = player->MovPos;
13420     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13421     return;
13422   }
13423   else if (!FrameReached(&screen_frame_counter, 1))
13424     return;
13425
13426   if (ScreenMovPos)
13427   {
13428     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13429     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13430     redraw_mask |= REDRAW_FIELD;
13431   }
13432   else
13433     ScreenMovDir = MV_NONE;
13434 }
13435
13436 void TestIfPlayerTouchesCustomElement(int x, int y)
13437 {
13438   static int xy[4][2] =
13439   {
13440     { 0, -1 },
13441     { -1, 0 },
13442     { +1, 0 },
13443     { 0, +1 }
13444   };
13445   static int trigger_sides[4][2] =
13446   {
13447     /* center side       border side */
13448     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13449     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13450     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13451     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13452   };
13453   static int touch_dir[4] =
13454   {
13455     MV_LEFT | MV_RIGHT,
13456     MV_UP   | MV_DOWN,
13457     MV_UP   | MV_DOWN,
13458     MV_LEFT | MV_RIGHT
13459   };
13460   int center_element = Feld[x][y];      /* should always be non-moving! */
13461   int i;
13462
13463   for (i = 0; i < NUM_DIRECTIONS; i++)
13464   {
13465     int xx = x + xy[i][0];
13466     int yy = y + xy[i][1];
13467     int center_side = trigger_sides[i][0];
13468     int border_side = trigger_sides[i][1];
13469     int border_element;
13470
13471     if (!IN_LEV_FIELD(xx, yy))
13472       continue;
13473
13474     if (IS_PLAYER(x, y))                /* player found at center element */
13475     {
13476       struct PlayerInfo *player = PLAYERINFO(x, y);
13477
13478       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13479         border_element = Feld[xx][yy];          /* may be moving! */
13480       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13481         border_element = Feld[xx][yy];
13482       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13483         border_element = MovingOrBlocked2Element(xx, yy);
13484       else
13485         continue;               /* center and border element do not touch */
13486
13487       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13488                                  player->index_bit, border_side);
13489       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13490                                           CE_PLAYER_TOUCHES_X,
13491                                           player->index_bit, border_side);
13492
13493 #if USE_FIX_CE_ACTION_WITH_PLAYER
13494       {
13495         /* use player element that is initially defined in the level playfield,
13496            not the player element that corresponds to the runtime player number
13497            (example: a level that contains EL_PLAYER_3 as the only player would
13498            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13499         int player_element = PLAYERINFO(x, y)->initial_element;
13500
13501         CheckElementChangeBySide(xx, yy, border_element, player_element,
13502                                  CE_TOUCHING_X, border_side);
13503       }
13504 #endif
13505     }
13506     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13507     {
13508       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13509
13510       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13511       {
13512         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13513           continue;             /* center and border element do not touch */
13514       }
13515
13516       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13517                                  player->index_bit, center_side);
13518       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13519                                           CE_PLAYER_TOUCHES_X,
13520                                           player->index_bit, center_side);
13521
13522 #if USE_FIX_CE_ACTION_WITH_PLAYER
13523       {
13524         /* use player element that is initially defined in the level playfield,
13525            not the player element that corresponds to the runtime player number
13526            (example: a level that contains EL_PLAYER_3 as the only player would
13527            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13528         int player_element = PLAYERINFO(xx, yy)->initial_element;
13529
13530         CheckElementChangeBySide(x, y, center_element, player_element,
13531                                  CE_TOUCHING_X, center_side);
13532       }
13533 #endif
13534
13535       break;
13536     }
13537   }
13538 }
13539
13540 #if USE_ELEMENT_TOUCHING_BUGFIX
13541
13542 void TestIfElementTouchesCustomElement(int x, int y)
13543 {
13544   static int xy[4][2] =
13545   {
13546     { 0, -1 },
13547     { -1, 0 },
13548     { +1, 0 },
13549     { 0, +1 }
13550   };
13551   static int trigger_sides[4][2] =
13552   {
13553     /* center side      border side */
13554     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13555     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13556     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13557     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13558   };
13559   static int touch_dir[4] =
13560   {
13561     MV_LEFT | MV_RIGHT,
13562     MV_UP   | MV_DOWN,
13563     MV_UP   | MV_DOWN,
13564     MV_LEFT | MV_RIGHT
13565   };
13566   boolean change_center_element = FALSE;
13567   int center_element = Feld[x][y];      /* should always be non-moving! */
13568   int border_element_old[NUM_DIRECTIONS];
13569   int i;
13570
13571   for (i = 0; i < NUM_DIRECTIONS; i++)
13572   {
13573     int xx = x + xy[i][0];
13574     int yy = y + xy[i][1];
13575     int border_element;
13576
13577     border_element_old[i] = -1;
13578
13579     if (!IN_LEV_FIELD(xx, yy))
13580       continue;
13581
13582     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13583       border_element = Feld[xx][yy];    /* may be moving! */
13584     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13585       border_element = Feld[xx][yy];
13586     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13587       border_element = MovingOrBlocked2Element(xx, yy);
13588     else
13589       continue;                 /* center and border element do not touch */
13590
13591     border_element_old[i] = border_element;
13592   }
13593
13594   for (i = 0; i < NUM_DIRECTIONS; i++)
13595   {
13596     int xx = x + xy[i][0];
13597     int yy = y + xy[i][1];
13598     int center_side = trigger_sides[i][0];
13599     int border_element = border_element_old[i];
13600
13601     if (border_element == -1)
13602       continue;
13603
13604     /* check for change of border element */
13605     CheckElementChangeBySide(xx, yy, border_element, center_element,
13606                              CE_TOUCHING_X, center_side);
13607   }
13608
13609   for (i = 0; i < NUM_DIRECTIONS; i++)
13610   {
13611     int xx = x + xy[i][0];
13612     int yy = y + xy[i][1];
13613     int border_side = trigger_sides[i][1];
13614     int border_element = border_element_old[i];
13615
13616     if (border_element == -1)
13617       continue;
13618
13619     /* check for change of center element (but change it only once) */
13620     if (!change_center_element)
13621       change_center_element =
13622         CheckElementChangeBySide(x, y, center_element, border_element,
13623                                  CE_TOUCHING_X, border_side);
13624
13625 #if USE_FIX_CE_ACTION_WITH_PLAYER
13626     if (IS_PLAYER(xx, yy))
13627     {
13628       /* use player element that is initially defined in the level playfield,
13629          not the player element that corresponds to the runtime player number
13630          (example: a level that contains EL_PLAYER_3 as the only player would
13631          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13632       int player_element = PLAYERINFO(xx, yy)->initial_element;
13633
13634       CheckElementChangeBySide(x, y, center_element, player_element,
13635                                CE_TOUCHING_X, border_side);
13636     }
13637 #endif
13638   }
13639 }
13640
13641 #else
13642
13643 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13644 {
13645   static int xy[4][2] =
13646   {
13647     { 0, -1 },
13648     { -1, 0 },
13649     { +1, 0 },
13650     { 0, +1 }
13651   };
13652   static int trigger_sides[4][2] =
13653   {
13654     /* center side      border side */
13655     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13656     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13657     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13658     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13659   };
13660   static int touch_dir[4] =
13661   {
13662     MV_LEFT | MV_RIGHT,
13663     MV_UP   | MV_DOWN,
13664     MV_UP   | MV_DOWN,
13665     MV_LEFT | MV_RIGHT
13666   };
13667   boolean change_center_element = FALSE;
13668   int center_element = Feld[x][y];      /* should always be non-moving! */
13669   int i;
13670
13671   for (i = 0; i < NUM_DIRECTIONS; i++)
13672   {
13673     int xx = x + xy[i][0];
13674     int yy = y + xy[i][1];
13675     int center_side = trigger_sides[i][0];
13676     int border_side = trigger_sides[i][1];
13677     int border_element;
13678
13679     if (!IN_LEV_FIELD(xx, yy))
13680       continue;
13681
13682     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13683       border_element = Feld[xx][yy];    /* may be moving! */
13684     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13685       border_element = Feld[xx][yy];
13686     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13687       border_element = MovingOrBlocked2Element(xx, yy);
13688     else
13689       continue;                 /* center and border element do not touch */
13690
13691     /* check for change of center element (but change it only once) */
13692     if (!change_center_element)
13693       change_center_element =
13694         CheckElementChangeBySide(x, y, center_element, border_element,
13695                                  CE_TOUCHING_X, border_side);
13696
13697     /* check for change of border element */
13698     CheckElementChangeBySide(xx, yy, border_element, center_element,
13699                              CE_TOUCHING_X, center_side);
13700   }
13701 }
13702
13703 #endif
13704
13705 void TestIfElementHitsCustomElement(int x, int y, int direction)
13706 {
13707   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13708   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13709   int hitx = x + dx, hity = y + dy;
13710   int hitting_element = Feld[x][y];
13711   int touched_element;
13712
13713   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13714     return;
13715
13716   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13717                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13718
13719   if (IN_LEV_FIELD(hitx, hity))
13720   {
13721     int opposite_direction = MV_DIR_OPPOSITE(direction);
13722     int hitting_side = direction;
13723     int touched_side = opposite_direction;
13724     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13725                           MovDir[hitx][hity] != direction ||
13726                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13727
13728     object_hit = TRUE;
13729
13730     if (object_hit)
13731     {
13732       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13733                                CE_HITTING_X, touched_side);
13734
13735       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13736                                CE_HIT_BY_X, hitting_side);
13737
13738       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13739                                CE_HIT_BY_SOMETHING, opposite_direction);
13740
13741 #if USE_FIX_CE_ACTION_WITH_PLAYER
13742       if (IS_PLAYER(hitx, hity))
13743       {
13744         /* use player element that is initially defined in the level playfield,
13745            not the player element that corresponds to the runtime player number
13746            (example: a level that contains EL_PLAYER_3 as the only player would
13747            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13748         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13749
13750         CheckElementChangeBySide(x, y, hitting_element, player_element,
13751                                  CE_HITTING_X, touched_side);
13752       }
13753 #endif
13754     }
13755   }
13756
13757   /* "hitting something" is also true when hitting the playfield border */
13758   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13759                            CE_HITTING_SOMETHING, direction);
13760 }
13761
13762 #if 0
13763 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13764 {
13765   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13766   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13767   int hitx = x + dx, hity = y + dy;
13768   int hitting_element = Feld[x][y];
13769   int touched_element;
13770 #if 0
13771   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13772                         !IS_FREE(hitx, hity) &&
13773                         (!IS_MOVING(hitx, hity) ||
13774                          MovDir[hitx][hity] != direction ||
13775                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13776 #endif
13777
13778   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13779     return;
13780
13781 #if 0
13782   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13783     return;
13784 #endif
13785
13786   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13787                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13788
13789   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13790                            EP_CAN_SMASH_EVERYTHING, direction);
13791
13792   if (IN_LEV_FIELD(hitx, hity))
13793   {
13794     int opposite_direction = MV_DIR_OPPOSITE(direction);
13795     int hitting_side = direction;
13796     int touched_side = opposite_direction;
13797 #if 0
13798     int touched_element = MovingOrBlocked2Element(hitx, hity);
13799 #endif
13800 #if 1
13801     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13802                           MovDir[hitx][hity] != direction ||
13803                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13804
13805     object_hit = TRUE;
13806 #endif
13807
13808     if (object_hit)
13809     {
13810       int i;
13811
13812       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13813                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13814
13815       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13816                                CE_OTHER_IS_SMASHING, touched_side);
13817
13818       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13819                                CE_OTHER_GETS_SMASHED, hitting_side);
13820     }
13821   }
13822 }
13823 #endif
13824
13825 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13826 {
13827   int i, kill_x = -1, kill_y = -1;
13828
13829   int bad_element = -1;
13830   static int test_xy[4][2] =
13831   {
13832     { 0, -1 },
13833     { -1, 0 },
13834     { +1, 0 },
13835     { 0, +1 }
13836   };
13837   static int test_dir[4] =
13838   {
13839     MV_UP,
13840     MV_LEFT,
13841     MV_RIGHT,
13842     MV_DOWN
13843   };
13844
13845   for (i = 0; i < NUM_DIRECTIONS; i++)
13846   {
13847     int test_x, test_y, test_move_dir, test_element;
13848
13849     test_x = good_x + test_xy[i][0];
13850     test_y = good_y + test_xy[i][1];
13851
13852     if (!IN_LEV_FIELD(test_x, test_y))
13853       continue;
13854
13855     test_move_dir =
13856       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13857
13858     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13859
13860     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13861        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13862     */
13863     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13864         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13865     {
13866       kill_x = test_x;
13867       kill_y = test_y;
13868       bad_element = test_element;
13869
13870       break;
13871     }
13872   }
13873
13874   if (kill_x != -1 || kill_y != -1)
13875   {
13876     if (IS_PLAYER(good_x, good_y))
13877     {
13878       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13879
13880       if (player->shield_deadly_time_left > 0 &&
13881           !IS_INDESTRUCTIBLE(bad_element))
13882         Bang(kill_x, kill_y);
13883       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13884         KillPlayer(player);
13885     }
13886     else
13887       Bang(good_x, good_y);
13888   }
13889 }
13890
13891 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13892 {
13893   int i, kill_x = -1, kill_y = -1;
13894   int bad_element = Feld[bad_x][bad_y];
13895   static int test_xy[4][2] =
13896   {
13897     { 0, -1 },
13898     { -1, 0 },
13899     { +1, 0 },
13900     { 0, +1 }
13901   };
13902   static int touch_dir[4] =
13903   {
13904     MV_LEFT | MV_RIGHT,
13905     MV_UP   | MV_DOWN,
13906     MV_UP   | MV_DOWN,
13907     MV_LEFT | MV_RIGHT
13908   };
13909   static int test_dir[4] =
13910   {
13911     MV_UP,
13912     MV_LEFT,
13913     MV_RIGHT,
13914     MV_DOWN
13915   };
13916
13917   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13918     return;
13919
13920   for (i = 0; i < NUM_DIRECTIONS; i++)
13921   {
13922     int test_x, test_y, test_move_dir, test_element;
13923
13924     test_x = bad_x + test_xy[i][0];
13925     test_y = bad_y + test_xy[i][1];
13926
13927     if (!IN_LEV_FIELD(test_x, test_y))
13928       continue;
13929
13930     test_move_dir =
13931       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13932
13933     test_element = Feld[test_x][test_y];
13934
13935     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13936        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13937     */
13938     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13939         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13940     {
13941       /* good thing is player or penguin that does not move away */
13942       if (IS_PLAYER(test_x, test_y))
13943       {
13944         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13945
13946         if (bad_element == EL_ROBOT && player->is_moving)
13947           continue;     /* robot does not kill player if he is moving */
13948
13949         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13950         {
13951           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13952             continue;           /* center and border element do not touch */
13953         }
13954
13955         kill_x = test_x;
13956         kill_y = test_y;
13957
13958         break;
13959       }
13960       else if (test_element == EL_PENGUIN)
13961       {
13962         kill_x = test_x;
13963         kill_y = test_y;
13964
13965         break;
13966       }
13967     }
13968   }
13969
13970   if (kill_x != -1 || kill_y != -1)
13971   {
13972     if (IS_PLAYER(kill_x, kill_y))
13973     {
13974       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13975
13976       if (player->shield_deadly_time_left > 0 &&
13977           !IS_INDESTRUCTIBLE(bad_element))
13978         Bang(bad_x, bad_y);
13979       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13980         KillPlayer(player);
13981     }
13982     else
13983       Bang(kill_x, kill_y);
13984   }
13985 }
13986
13987 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13988 {
13989   int bad_element = Feld[bad_x][bad_y];
13990   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13991   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13992   int test_x = bad_x + dx, test_y = bad_y + dy;
13993   int test_move_dir, test_element;
13994   int kill_x = -1, kill_y = -1;
13995
13996   if (!IN_LEV_FIELD(test_x, test_y))
13997     return;
13998
13999   test_move_dir =
14000     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14001
14002   test_element = Feld[test_x][test_y];
14003
14004   if (test_move_dir != bad_move_dir)
14005   {
14006     /* good thing can be player or penguin that does not move away */
14007     if (IS_PLAYER(test_x, test_y))
14008     {
14009       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14010
14011       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14012          player as being hit when he is moving towards the bad thing, because
14013          the "get hit by" condition would be lost after the player stops) */
14014       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14015         return;         /* player moves away from bad thing */
14016
14017       kill_x = test_x;
14018       kill_y = test_y;
14019     }
14020     else if (test_element == EL_PENGUIN)
14021     {
14022       kill_x = test_x;
14023       kill_y = test_y;
14024     }
14025   }
14026
14027   if (kill_x != -1 || kill_y != -1)
14028   {
14029     if (IS_PLAYER(kill_x, kill_y))
14030     {
14031       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14032
14033       if (player->shield_deadly_time_left > 0 &&
14034           !IS_INDESTRUCTIBLE(bad_element))
14035         Bang(bad_x, bad_y);
14036       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14037         KillPlayer(player);
14038     }
14039     else
14040       Bang(kill_x, kill_y);
14041   }
14042 }
14043
14044 void TestIfPlayerTouchesBadThing(int x, int y)
14045 {
14046   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14047 }
14048
14049 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14050 {
14051   TestIfGoodThingHitsBadThing(x, y, move_dir);
14052 }
14053
14054 void TestIfBadThingTouchesPlayer(int x, int y)
14055 {
14056   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14057 }
14058
14059 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14060 {
14061   TestIfBadThingHitsGoodThing(x, y, move_dir);
14062 }
14063
14064 void TestIfFriendTouchesBadThing(int x, int y)
14065 {
14066   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14067 }
14068
14069 void TestIfBadThingTouchesFriend(int x, int y)
14070 {
14071   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14072 }
14073
14074 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14075 {
14076   int i, kill_x = bad_x, kill_y = bad_y;
14077   static int xy[4][2] =
14078   {
14079     { 0, -1 },
14080     { -1, 0 },
14081     { +1, 0 },
14082     { 0, +1 }
14083   };
14084
14085   for (i = 0; i < NUM_DIRECTIONS; i++)
14086   {
14087     int x, y, element;
14088
14089     x = bad_x + xy[i][0];
14090     y = bad_y + xy[i][1];
14091     if (!IN_LEV_FIELD(x, y))
14092       continue;
14093
14094     element = Feld[x][y];
14095     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14096         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14097     {
14098       kill_x = x;
14099       kill_y = y;
14100       break;
14101     }
14102   }
14103
14104   if (kill_x != bad_x || kill_y != bad_y)
14105     Bang(bad_x, bad_y);
14106 }
14107
14108 void KillPlayer(struct PlayerInfo *player)
14109 {
14110   int jx = player->jx, jy = player->jy;
14111
14112   if (!player->active)
14113     return;
14114
14115   /* the following code was introduced to prevent an infinite loop when calling
14116      -> Bang()
14117      -> CheckTriggeredElementChangeExt()
14118      -> ExecuteCustomElementAction()
14119      -> KillPlayer()
14120      -> (infinitely repeating the above sequence of function calls)
14121      which occurs when killing the player while having a CE with the setting
14122      "kill player X when explosion of <player X>"; the solution using a new
14123      field "player->killed" was chosen for backwards compatibility, although
14124      clever use of the fields "player->active" etc. would probably also work */
14125 #if 1
14126   if (player->killed)
14127     return;
14128 #endif
14129
14130   player->killed = TRUE;
14131
14132   /* remove accessible field at the player's position */
14133   Feld[jx][jy] = EL_EMPTY;
14134
14135   /* deactivate shield (else Bang()/Explode() would not work right) */
14136   player->shield_normal_time_left = 0;
14137   player->shield_deadly_time_left = 0;
14138
14139   Bang(jx, jy);
14140   BuryPlayer(player);
14141 }
14142
14143 static void KillPlayerUnlessEnemyProtected(int x, int y)
14144 {
14145   if (!PLAYER_ENEMY_PROTECTED(x, y))
14146     KillPlayer(PLAYERINFO(x, y));
14147 }
14148
14149 static void KillPlayerUnlessExplosionProtected(int x, int y)
14150 {
14151   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14152     KillPlayer(PLAYERINFO(x, y));
14153 }
14154
14155 void BuryPlayer(struct PlayerInfo *player)
14156 {
14157   int jx = player->jx, jy = player->jy;
14158
14159   if (!player->active)
14160     return;
14161
14162   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14163   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14164
14165   player->GameOver = TRUE;
14166   RemovePlayer(player);
14167 }
14168
14169 void RemovePlayer(struct PlayerInfo *player)
14170 {
14171   int jx = player->jx, jy = player->jy;
14172   int i, found = FALSE;
14173
14174   player->present = FALSE;
14175   player->active = FALSE;
14176
14177   if (!ExplodeField[jx][jy])
14178     StorePlayer[jx][jy] = 0;
14179
14180   if (player->is_moving)
14181     TEST_DrawLevelField(player->last_jx, player->last_jy);
14182
14183   for (i = 0; i < MAX_PLAYERS; i++)
14184     if (stored_player[i].active)
14185       found = TRUE;
14186
14187   if (!found)
14188     AllPlayersGone = TRUE;
14189
14190   ExitX = ZX = jx;
14191   ExitY = ZY = jy;
14192 }
14193
14194 #if USE_NEW_SNAP_DELAY
14195 static void setFieldForSnapping(int x, int y, int element, int direction)
14196 {
14197   struct ElementInfo *ei = &element_info[element];
14198   int direction_bit = MV_DIR_TO_BIT(direction);
14199   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14200   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14201                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14202
14203   Feld[x][y] = EL_ELEMENT_SNAPPING;
14204   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14205
14206   ResetGfxAnimation(x, y);
14207
14208   GfxElement[x][y] = element;
14209   GfxAction[x][y] = action;
14210   GfxDir[x][y] = direction;
14211   GfxFrame[x][y] = -1;
14212 }
14213 #endif
14214
14215 /*
14216   =============================================================================
14217   checkDiagonalPushing()
14218   -----------------------------------------------------------------------------
14219   check if diagonal input device direction results in pushing of object
14220   (by checking if the alternative direction is walkable, diggable, ...)
14221   =============================================================================
14222 */
14223
14224 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14225                                     int x, int y, int real_dx, int real_dy)
14226 {
14227   int jx, jy, dx, dy, xx, yy;
14228
14229   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14230     return TRUE;
14231
14232   /* diagonal direction: check alternative direction */
14233   jx = player->jx;
14234   jy = player->jy;
14235   dx = x - jx;
14236   dy = y - jy;
14237   xx = jx + (dx == 0 ? real_dx : 0);
14238   yy = jy + (dy == 0 ? real_dy : 0);
14239
14240   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14241 }
14242
14243 /*
14244   =============================================================================
14245   DigField()
14246   -----------------------------------------------------------------------------
14247   x, y:                 field next to player (non-diagonal) to try to dig to
14248   real_dx, real_dy:     direction as read from input device (can be diagonal)
14249   =============================================================================
14250 */
14251
14252 static int DigField(struct PlayerInfo *player,
14253                     int oldx, int oldy, int x, int y,
14254                     int real_dx, int real_dy, int mode)
14255 {
14256   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14257   boolean player_was_pushing = player->is_pushing;
14258   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14259   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14260   int jx = oldx, jy = oldy;
14261   int dx = x - jx, dy = y - jy;
14262   int nextx = x + dx, nexty = y + dy;
14263   int move_direction = (dx == -1 ? MV_LEFT  :
14264                         dx == +1 ? MV_RIGHT :
14265                         dy == -1 ? MV_UP    :
14266                         dy == +1 ? MV_DOWN  : MV_NONE);
14267   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14268   int dig_side = MV_DIR_OPPOSITE(move_direction);
14269   int old_element = Feld[jx][jy];
14270 #if USE_FIXED_DONT_RUN_INTO
14271   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14272 #else
14273   int element;
14274 #endif
14275   int collect_count;
14276
14277   if (is_player)                /* function can also be called by EL_PENGUIN */
14278   {
14279     if (player->MovPos == 0)
14280     {
14281       player->is_digging = FALSE;
14282       player->is_collecting = FALSE;
14283     }
14284
14285     if (player->MovPos == 0)    /* last pushing move finished */
14286       player->is_pushing = FALSE;
14287
14288     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14289     {
14290       player->is_switching = FALSE;
14291       player->push_delay = -1;
14292
14293       return MP_NO_ACTION;
14294     }
14295   }
14296
14297 #if !USE_FIXED_DONT_RUN_INTO
14298   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14299     return MP_NO_ACTION;
14300 #endif
14301
14302   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14303     old_element = Back[jx][jy];
14304
14305   /* in case of element dropped at player position, check background */
14306   else if (Back[jx][jy] != EL_EMPTY &&
14307            game.engine_version >= VERSION_IDENT(2,2,0,0))
14308     old_element = Back[jx][jy];
14309
14310   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14311     return MP_NO_ACTION;        /* field has no opening in this direction */
14312
14313   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14314     return MP_NO_ACTION;        /* field has no opening in this direction */
14315
14316 #if USE_FIXED_DONT_RUN_INTO
14317   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14318   {
14319     SplashAcid(x, y);
14320
14321     Feld[jx][jy] = player->artwork_element;
14322     InitMovingField(jx, jy, MV_DOWN);
14323     Store[jx][jy] = EL_ACID;
14324     ContinueMoving(jx, jy);
14325     BuryPlayer(player);
14326
14327     return MP_DONT_RUN_INTO;
14328   }
14329 #endif
14330
14331 #if USE_FIXED_DONT_RUN_INTO
14332   if (player_can_move && DONT_RUN_INTO(element))
14333   {
14334     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14335
14336     return MP_DONT_RUN_INTO;
14337   }
14338 #endif
14339
14340 #if USE_FIXED_DONT_RUN_INTO
14341   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14342     return MP_NO_ACTION;
14343 #endif
14344
14345 #if !USE_FIXED_DONT_RUN_INTO
14346   element = Feld[x][y];
14347 #endif
14348
14349   collect_count = element_info[element].collect_count_initial;
14350
14351   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14352     return MP_NO_ACTION;
14353
14354   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14355     player_can_move = player_can_move_or_snap;
14356
14357   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14358       game.engine_version >= VERSION_IDENT(2,2,0,0))
14359   {
14360     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14361                                player->index_bit, dig_side);
14362     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14363                                         player->index_bit, dig_side);
14364
14365     if (element == EL_DC_LANDMINE)
14366       Bang(x, y);
14367
14368     if (Feld[x][y] != element)          /* field changed by snapping */
14369       return MP_ACTION;
14370
14371     return MP_NO_ACTION;
14372   }
14373
14374 #if USE_PLAYER_GRAVITY
14375   if (player->gravity && is_player && !player->is_auto_moving &&
14376       canFallDown(player) && move_direction != MV_DOWN &&
14377       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14378     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14379 #else
14380   if (game.gravity && is_player && !player->is_auto_moving &&
14381       canFallDown(player) && move_direction != MV_DOWN &&
14382       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14383     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14384 #endif
14385
14386   if (player_can_move &&
14387       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14388   {
14389     int sound_element = SND_ELEMENT(element);
14390     int sound_action = ACTION_WALKING;
14391
14392     if (IS_RND_GATE(element))
14393     {
14394       if (!player->key[RND_GATE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_RND_GATE_GRAY(element))
14398     {
14399       if (!player->key[RND_GATE_GRAY_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14403     {
14404       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (element == EL_EXIT_OPEN ||
14408              element == EL_EM_EXIT_OPEN ||
14409              element == EL_STEEL_EXIT_OPEN ||
14410              element == EL_EM_STEEL_EXIT_OPEN ||
14411              element == EL_SP_EXIT_OPEN ||
14412              element == EL_SP_EXIT_OPENING)
14413     {
14414       sound_action = ACTION_PASSING;    /* player is passing exit */
14415     }
14416     else if (element == EL_EMPTY)
14417     {
14418       sound_action = ACTION_MOVING;             /* nothing to walk on */
14419     }
14420
14421     /* play sound from background or player, whatever is available */
14422     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14423       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14424     else
14425       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14426   }
14427   else if (player_can_move &&
14428            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14429   {
14430     if (!ACCESS_FROM(element, opposite_direction))
14431       return MP_NO_ACTION;      /* field not accessible from this direction */
14432
14433     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14434       return MP_NO_ACTION;
14435
14436     if (IS_EM_GATE(element))
14437     {
14438       if (!player->key[EM_GATE_NR(element)])
14439         return MP_NO_ACTION;
14440     }
14441     else if (IS_EM_GATE_GRAY(element))
14442     {
14443       if (!player->key[EM_GATE_GRAY_NR(element)])
14444         return MP_NO_ACTION;
14445     }
14446     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14447     {
14448       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14449         return MP_NO_ACTION;
14450     }
14451     else if (IS_EMC_GATE(element))
14452     {
14453       if (!player->key[EMC_GATE_NR(element)])
14454         return MP_NO_ACTION;
14455     }
14456     else if (IS_EMC_GATE_GRAY(element))
14457     {
14458       if (!player->key[EMC_GATE_GRAY_NR(element)])
14459         return MP_NO_ACTION;
14460     }
14461     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14462     {
14463       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14464         return MP_NO_ACTION;
14465     }
14466     else if (element == EL_DC_GATE_WHITE ||
14467              element == EL_DC_GATE_WHITE_GRAY ||
14468              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14469     {
14470       if (player->num_white_keys == 0)
14471         return MP_NO_ACTION;
14472
14473       player->num_white_keys--;
14474     }
14475     else if (IS_SP_PORT(element))
14476     {
14477       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14478           element == EL_SP_GRAVITY_PORT_RIGHT ||
14479           element == EL_SP_GRAVITY_PORT_UP ||
14480           element == EL_SP_GRAVITY_PORT_DOWN)
14481 #if USE_PLAYER_GRAVITY
14482         player->gravity = !player->gravity;
14483 #else
14484         game.gravity = !game.gravity;
14485 #endif
14486       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14487                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14488                element == EL_SP_GRAVITY_ON_PORT_UP ||
14489                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14490 #if USE_PLAYER_GRAVITY
14491         player->gravity = TRUE;
14492 #else
14493         game.gravity = TRUE;
14494 #endif
14495       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14496                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14497                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14498                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14499 #if USE_PLAYER_GRAVITY
14500         player->gravity = FALSE;
14501 #else
14502         game.gravity = FALSE;
14503 #endif
14504     }
14505
14506     /* automatically move to the next field with double speed */
14507     player->programmed_action = move_direction;
14508
14509     if (player->move_delay_reset_counter == 0)
14510     {
14511       player->move_delay_reset_counter = 2;     /* two double speed steps */
14512
14513       DOUBLE_PLAYER_SPEED(player);
14514     }
14515
14516     PlayLevelSoundAction(x, y, ACTION_PASSING);
14517   }
14518   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14519   {
14520     RemoveField(x, y);
14521
14522     if (mode != DF_SNAP)
14523     {
14524       GfxElement[x][y] = GFX_ELEMENT(element);
14525       player->is_digging = TRUE;
14526     }
14527
14528     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14529
14530     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14531                                         player->index_bit, dig_side);
14532
14533     if (mode == DF_SNAP)
14534     {
14535 #if USE_NEW_SNAP_DELAY
14536       if (level.block_snap_field)
14537         setFieldForSnapping(x, y, element, move_direction);
14538       else
14539         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14540 #else
14541       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14542 #endif
14543
14544       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14545                                           player->index_bit, dig_side);
14546     }
14547   }
14548   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14549   {
14550     RemoveField(x, y);
14551
14552     if (is_player && mode != DF_SNAP)
14553     {
14554       GfxElement[x][y] = element;
14555       player->is_collecting = TRUE;
14556     }
14557
14558     if (element == EL_SPEED_PILL)
14559     {
14560       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14561     }
14562     else if (element == EL_EXTRA_TIME && level.time > 0)
14563     {
14564       TimeLeft += level.extra_time;
14565
14566 #if 1
14567       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14568
14569       DisplayGameControlValues();
14570 #else
14571       DrawGameValue_Time(TimeLeft);
14572 #endif
14573     }
14574     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14575     {
14576       player->shield_normal_time_left += level.shield_normal_time;
14577       if (element == EL_SHIELD_DEADLY)
14578         player->shield_deadly_time_left += level.shield_deadly_time;
14579     }
14580     else if (element == EL_DYNAMITE ||
14581              element == EL_EM_DYNAMITE ||
14582              element == EL_SP_DISK_RED)
14583     {
14584       if (player->inventory_size < MAX_INVENTORY_SIZE)
14585         player->inventory_element[player->inventory_size++] = element;
14586
14587       DrawGameDoorValues();
14588     }
14589     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14590     {
14591       player->dynabomb_count++;
14592       player->dynabombs_left++;
14593     }
14594     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14595     {
14596       player->dynabomb_size++;
14597     }
14598     else if (element == EL_DYNABOMB_INCREASE_POWER)
14599     {
14600       player->dynabomb_xl = TRUE;
14601     }
14602     else if (IS_KEY(element))
14603     {
14604       player->key[KEY_NR(element)] = TRUE;
14605
14606       DrawGameDoorValues();
14607     }
14608     else if (element == EL_DC_KEY_WHITE)
14609     {
14610       player->num_white_keys++;
14611
14612       /* display white keys? */
14613       /* DrawGameDoorValues(); */
14614     }
14615     else if (IS_ENVELOPE(element))
14616     {
14617       player->show_envelope = element;
14618     }
14619     else if (element == EL_EMC_LENSES)
14620     {
14621       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14622
14623       RedrawAllInvisibleElementsForLenses();
14624     }
14625     else if (element == EL_EMC_MAGNIFIER)
14626     {
14627       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14628
14629       RedrawAllInvisibleElementsForMagnifier();
14630     }
14631     else if (IS_DROPPABLE(element) ||
14632              IS_THROWABLE(element))     /* can be collected and dropped */
14633     {
14634       int i;
14635
14636       if (collect_count == 0)
14637         player->inventory_infinite_element = element;
14638       else
14639         for (i = 0; i < collect_count; i++)
14640           if (player->inventory_size < MAX_INVENTORY_SIZE)
14641             player->inventory_element[player->inventory_size++] = element;
14642
14643       DrawGameDoorValues();
14644     }
14645     else if (collect_count > 0)
14646     {
14647       local_player->gems_still_needed -= collect_count;
14648       if (local_player->gems_still_needed < 0)
14649         local_player->gems_still_needed = 0;
14650
14651 #if 1
14652       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14653
14654       DisplayGameControlValues();
14655 #else
14656       DrawGameValue_Emeralds(local_player->gems_still_needed);
14657 #endif
14658     }
14659
14660     RaiseScoreElement(element);
14661     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14662
14663     if (is_player)
14664       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14665                                           player->index_bit, dig_side);
14666
14667     if (mode == DF_SNAP)
14668     {
14669 #if USE_NEW_SNAP_DELAY
14670       if (level.block_snap_field)
14671         setFieldForSnapping(x, y, element, move_direction);
14672       else
14673         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14674 #else
14675       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14676 #endif
14677
14678       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14679                                           player->index_bit, dig_side);
14680     }
14681   }
14682   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14683   {
14684     if (mode == DF_SNAP && element != EL_BD_ROCK)
14685       return MP_NO_ACTION;
14686
14687     if (CAN_FALL(element) && dy)
14688       return MP_NO_ACTION;
14689
14690     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14691         !(element == EL_SPRING && level.use_spring_bug))
14692       return MP_NO_ACTION;
14693
14694     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14695         ((move_direction & MV_VERTICAL &&
14696           ((element_info[element].move_pattern & MV_LEFT &&
14697             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14698            (element_info[element].move_pattern & MV_RIGHT &&
14699             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14700          (move_direction & MV_HORIZONTAL &&
14701           ((element_info[element].move_pattern & MV_UP &&
14702             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14703            (element_info[element].move_pattern & MV_DOWN &&
14704             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14705       return MP_NO_ACTION;
14706
14707     /* do not push elements already moving away faster than player */
14708     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14709         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14710       return MP_NO_ACTION;
14711
14712     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14713     {
14714       if (player->push_delay_value == -1 || !player_was_pushing)
14715         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14716     }
14717     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14718     {
14719       if (player->push_delay_value == -1)
14720         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14721     }
14722     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14723     {
14724       if (!player->is_pushing)
14725         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14726     }
14727
14728     player->is_pushing = TRUE;
14729     player->is_active = TRUE;
14730
14731     if (!(IN_LEV_FIELD(nextx, nexty) &&
14732           (IS_FREE(nextx, nexty) ||
14733            (IS_SB_ELEMENT(element) &&
14734             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14735            (IS_CUSTOM_ELEMENT(element) &&
14736             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14737       return MP_NO_ACTION;
14738
14739     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14740       return MP_NO_ACTION;
14741
14742     if (player->push_delay == -1)       /* new pushing; restart delay */
14743       player->push_delay = 0;
14744
14745     if (player->push_delay < player->push_delay_value &&
14746         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14747         element != EL_SPRING && element != EL_BALLOON)
14748     {
14749       /* make sure that there is no move delay before next try to push */
14750       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14751         player->move_delay = 0;
14752
14753       return MP_NO_ACTION;
14754     }
14755
14756     if (IS_CUSTOM_ELEMENT(element) &&
14757         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14758     {
14759       if (!DigFieldByCE(nextx, nexty, element))
14760         return MP_NO_ACTION;
14761     }
14762
14763     if (IS_SB_ELEMENT(element))
14764     {
14765       if (element == EL_SOKOBAN_FIELD_FULL)
14766       {
14767         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14768         local_player->sokobanfields_still_needed++;
14769       }
14770
14771       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14772       {
14773         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14774         local_player->sokobanfields_still_needed--;
14775       }
14776
14777       Feld[x][y] = EL_SOKOBAN_OBJECT;
14778
14779       if (Back[x][y] == Back[nextx][nexty])
14780         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14781       else if (Back[x][y] != 0)
14782         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14783                                     ACTION_EMPTYING);
14784       else
14785         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14786                                     ACTION_FILLING);
14787
14788       if (local_player->sokobanfields_still_needed == 0 &&
14789           game.emulation == EMU_SOKOBAN)
14790       {
14791         PlayerWins(player);
14792
14793         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14794       }
14795     }
14796     else
14797       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14798
14799     InitMovingField(x, y, move_direction);
14800     GfxAction[x][y] = ACTION_PUSHING;
14801
14802     if (mode == DF_SNAP)
14803       ContinueMoving(x, y);
14804     else
14805       MovPos[x][y] = (dx != 0 ? dx : dy);
14806
14807     Pushed[x][y] = TRUE;
14808     Pushed[nextx][nexty] = TRUE;
14809
14810     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14811       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14812     else
14813       player->push_delay_value = -1;    /* get new value later */
14814
14815     /* check for element change _after_ element has been pushed */
14816     if (game.use_change_when_pushing_bug)
14817     {
14818       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14819                                  player->index_bit, dig_side);
14820       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14821                                           player->index_bit, dig_side);
14822     }
14823   }
14824   else if (IS_SWITCHABLE(element))
14825   {
14826     if (PLAYER_SWITCHING(player, x, y))
14827     {
14828       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14829                                           player->index_bit, dig_side);
14830
14831       return MP_ACTION;
14832     }
14833
14834     player->is_switching = TRUE;
14835     player->switch_x = x;
14836     player->switch_y = y;
14837
14838     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14839
14840     if (element == EL_ROBOT_WHEEL)
14841     {
14842       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14843       ZX = x;
14844       ZY = y;
14845
14846       game.robot_wheel_active = TRUE;
14847
14848       TEST_DrawLevelField(x, y);
14849     }
14850     else if (element == EL_SP_TERMINAL)
14851     {
14852       int xx, yy;
14853
14854       SCAN_PLAYFIELD(xx, yy)
14855       {
14856         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14857           Bang(xx, yy);
14858         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14859           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14860       }
14861     }
14862     else if (IS_BELT_SWITCH(element))
14863     {
14864       ToggleBeltSwitch(x, y);
14865     }
14866     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14867              element == EL_SWITCHGATE_SWITCH_DOWN ||
14868              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14869              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14870     {
14871       ToggleSwitchgateSwitch(x, y);
14872     }
14873     else if (element == EL_LIGHT_SWITCH ||
14874              element == EL_LIGHT_SWITCH_ACTIVE)
14875     {
14876       ToggleLightSwitch(x, y);
14877     }
14878     else if (element == EL_TIMEGATE_SWITCH ||
14879              element == EL_DC_TIMEGATE_SWITCH)
14880     {
14881       ActivateTimegateSwitch(x, y);
14882     }
14883     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14884              element == EL_BALLOON_SWITCH_RIGHT ||
14885              element == EL_BALLOON_SWITCH_UP    ||
14886              element == EL_BALLOON_SWITCH_DOWN  ||
14887              element == EL_BALLOON_SWITCH_NONE  ||
14888              element == EL_BALLOON_SWITCH_ANY)
14889     {
14890       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14891                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14892                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14893                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14894                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14895                              move_direction);
14896     }
14897     else if (element == EL_LAMP)
14898     {
14899       Feld[x][y] = EL_LAMP_ACTIVE;
14900       local_player->lights_still_needed--;
14901
14902       ResetGfxAnimation(x, y);
14903       TEST_DrawLevelField(x, y);
14904     }
14905     else if (element == EL_TIME_ORB_FULL)
14906     {
14907       Feld[x][y] = EL_TIME_ORB_EMPTY;
14908
14909       if (level.time > 0 || level.use_time_orb_bug)
14910       {
14911         TimeLeft += level.time_orb_time;
14912
14913 #if 1
14914         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14915
14916         DisplayGameControlValues();
14917 #else
14918         DrawGameValue_Time(TimeLeft);
14919 #endif
14920       }
14921
14922       ResetGfxAnimation(x, y);
14923       TEST_DrawLevelField(x, y);
14924     }
14925     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14926              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14927     {
14928       int xx, yy;
14929
14930       game.ball_state = !game.ball_state;
14931
14932       SCAN_PLAYFIELD(xx, yy)
14933       {
14934         int e = Feld[xx][yy];
14935
14936         if (game.ball_state)
14937         {
14938           if (e == EL_EMC_MAGIC_BALL)
14939             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14940           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14941             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14942         }
14943         else
14944         {
14945           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14946             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14947           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14948             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14949         }
14950       }
14951     }
14952
14953     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14954                                         player->index_bit, dig_side);
14955
14956     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14957                                         player->index_bit, dig_side);
14958
14959     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14960                                         player->index_bit, dig_side);
14961
14962     return MP_ACTION;
14963   }
14964   else
14965   {
14966     if (!PLAYER_SWITCHING(player, x, y))
14967     {
14968       player->is_switching = TRUE;
14969       player->switch_x = x;
14970       player->switch_y = y;
14971
14972       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14973                                  player->index_bit, dig_side);
14974       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14975                                           player->index_bit, dig_side);
14976
14977       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14978                                  player->index_bit, dig_side);
14979       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14980                                           player->index_bit, dig_side);
14981     }
14982
14983     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14984                                player->index_bit, dig_side);
14985     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14986                                         player->index_bit, dig_side);
14987
14988     return MP_NO_ACTION;
14989   }
14990
14991   player->push_delay = -1;
14992
14993   if (is_player)                /* function can also be called by EL_PENGUIN */
14994   {
14995     if (Feld[x][y] != element)          /* really digged/collected something */
14996     {
14997       player->is_collecting = !player->is_digging;
14998       player->is_active = TRUE;
14999     }
15000   }
15001
15002   return MP_MOVING;
15003 }
15004
15005 static boolean DigFieldByCE(int x, int y, int digging_element)
15006 {
15007   int element = Feld[x][y];
15008
15009   if (!IS_FREE(x, y))
15010   {
15011     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15012                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15013                   ACTION_BREAKING);
15014
15015     /* no element can dig solid indestructible elements */
15016     if (IS_INDESTRUCTIBLE(element) &&
15017         !IS_DIGGABLE(element) &&
15018         !IS_COLLECTIBLE(element))
15019       return FALSE;
15020
15021     if (AmoebaNr[x][y] &&
15022         (element == EL_AMOEBA_FULL ||
15023          element == EL_BD_AMOEBA ||
15024          element == EL_AMOEBA_GROWING))
15025     {
15026       AmoebaCnt[AmoebaNr[x][y]]--;
15027       AmoebaCnt2[AmoebaNr[x][y]]--;
15028     }
15029
15030     if (IS_MOVING(x, y))
15031       RemoveMovingField(x, y);
15032     else
15033     {
15034       RemoveField(x, y);
15035       TEST_DrawLevelField(x, y);
15036     }
15037
15038     /* if digged element was about to explode, prevent the explosion */
15039     ExplodeField[x][y] = EX_TYPE_NONE;
15040
15041     PlayLevelSoundAction(x, y, action);
15042   }
15043
15044   Store[x][y] = EL_EMPTY;
15045
15046 #if 1
15047   /* this makes it possible to leave the removed element again */
15048   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15049     Store[x][y] = element;
15050 #else
15051   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15052   {
15053     int move_leave_element = element_info[digging_element].move_leave_element;
15054
15055     /* this makes it possible to leave the removed element again */
15056     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15057                    element : move_leave_element);
15058   }
15059 #endif
15060
15061   return TRUE;
15062 }
15063
15064 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15065 {
15066   int jx = player->jx, jy = player->jy;
15067   int x = jx + dx, y = jy + dy;
15068   int snap_direction = (dx == -1 ? MV_LEFT  :
15069                         dx == +1 ? MV_RIGHT :
15070                         dy == -1 ? MV_UP    :
15071                         dy == +1 ? MV_DOWN  : MV_NONE);
15072   boolean can_continue_snapping = (level.continuous_snapping &&
15073                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15074
15075   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15076     return FALSE;
15077
15078   if (!player->active || !IN_LEV_FIELD(x, y))
15079     return FALSE;
15080
15081   if (dx && dy)
15082     return FALSE;
15083
15084   if (!dx && !dy)
15085   {
15086     if (player->MovPos == 0)
15087       player->is_pushing = FALSE;
15088
15089     player->is_snapping = FALSE;
15090
15091     if (player->MovPos == 0)
15092     {
15093       player->is_moving = FALSE;
15094       player->is_digging = FALSE;
15095       player->is_collecting = FALSE;
15096     }
15097
15098     return FALSE;
15099   }
15100
15101 #if USE_NEW_CONTINUOUS_SNAPPING
15102   /* prevent snapping with already pressed snap key when not allowed */
15103   if (player->is_snapping && !can_continue_snapping)
15104     return FALSE;
15105 #else
15106   if (player->is_snapping)
15107     return FALSE;
15108 #endif
15109
15110   player->MovDir = snap_direction;
15111
15112   if (player->MovPos == 0)
15113   {
15114     player->is_moving = FALSE;
15115     player->is_digging = FALSE;
15116     player->is_collecting = FALSE;
15117   }
15118
15119   player->is_dropping = FALSE;
15120   player->is_dropping_pressed = FALSE;
15121   player->drop_pressed_delay = 0;
15122
15123   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15124     return FALSE;
15125
15126   player->is_snapping = TRUE;
15127   player->is_active = TRUE;
15128
15129   if (player->MovPos == 0)
15130   {
15131     player->is_moving = FALSE;
15132     player->is_digging = FALSE;
15133     player->is_collecting = FALSE;
15134   }
15135
15136   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15137     TEST_DrawLevelField(player->last_jx, player->last_jy);
15138
15139   TEST_DrawLevelField(x, y);
15140
15141   return TRUE;
15142 }
15143
15144 static boolean DropElement(struct PlayerInfo *player)
15145 {
15146   int old_element, new_element;
15147   int dropx = player->jx, dropy = player->jy;
15148   int drop_direction = player->MovDir;
15149   int drop_side = drop_direction;
15150 #if 1
15151   int drop_element = get_next_dropped_element(player);
15152 #else
15153   int drop_element = (player->inventory_size > 0 ?
15154                       player->inventory_element[player->inventory_size - 1] :
15155                       player->inventory_infinite_element != EL_UNDEFINED ?
15156                       player->inventory_infinite_element :
15157                       player->dynabombs_left > 0 ?
15158                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15159                       EL_UNDEFINED);
15160 #endif
15161
15162   player->is_dropping_pressed = TRUE;
15163
15164   /* do not drop an element on top of another element; when holding drop key
15165      pressed without moving, dropped element must move away before the next
15166      element can be dropped (this is especially important if the next element
15167      is dynamite, which can be placed on background for historical reasons) */
15168   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15169     return MP_ACTION;
15170
15171   if (IS_THROWABLE(drop_element))
15172   {
15173     dropx += GET_DX_FROM_DIR(drop_direction);
15174     dropy += GET_DY_FROM_DIR(drop_direction);
15175
15176     if (!IN_LEV_FIELD(dropx, dropy))
15177       return FALSE;
15178   }
15179
15180   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15181   new_element = drop_element;           /* default: no change when dropping */
15182
15183   /* check if player is active, not moving and ready to drop */
15184   if (!player->active || player->MovPos || player->drop_delay > 0)
15185     return FALSE;
15186
15187   /* check if player has anything that can be dropped */
15188   if (new_element == EL_UNDEFINED)
15189     return FALSE;
15190
15191   /* check if drop key was pressed long enough for EM style dynamite */
15192   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15193     return FALSE;
15194
15195   /* check if anything can be dropped at the current position */
15196   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15197     return FALSE;
15198
15199   /* collected custom elements can only be dropped on empty fields */
15200   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15201     return FALSE;
15202
15203   if (old_element != EL_EMPTY)
15204     Back[dropx][dropy] = old_element;   /* store old element on this field */
15205
15206   ResetGfxAnimation(dropx, dropy);
15207   ResetRandomAnimationValue(dropx, dropy);
15208
15209   if (player->inventory_size > 0 ||
15210       player->inventory_infinite_element != EL_UNDEFINED)
15211   {
15212     if (player->inventory_size > 0)
15213     {
15214       player->inventory_size--;
15215
15216       DrawGameDoorValues();
15217
15218       if (new_element == EL_DYNAMITE)
15219         new_element = EL_DYNAMITE_ACTIVE;
15220       else if (new_element == EL_EM_DYNAMITE)
15221         new_element = EL_EM_DYNAMITE_ACTIVE;
15222       else if (new_element == EL_SP_DISK_RED)
15223         new_element = EL_SP_DISK_RED_ACTIVE;
15224     }
15225
15226     Feld[dropx][dropy] = new_element;
15227
15228     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15229       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15230                           el2img(Feld[dropx][dropy]), 0);
15231
15232     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15233
15234     /* needed if previous element just changed to "empty" in the last frame */
15235     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15236
15237     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15238                                player->index_bit, drop_side);
15239     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15240                                         CE_PLAYER_DROPS_X,
15241                                         player->index_bit, drop_side);
15242
15243     TestIfElementTouchesCustomElement(dropx, dropy);
15244   }
15245   else          /* player is dropping a dyna bomb */
15246   {
15247     player->dynabombs_left--;
15248
15249     Feld[dropx][dropy] = new_element;
15250
15251     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15252       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15253                           el2img(Feld[dropx][dropy]), 0);
15254
15255     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15256   }
15257
15258   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15259     InitField_WithBug1(dropx, dropy, FALSE);
15260
15261   new_element = Feld[dropx][dropy];     /* element might have changed */
15262
15263   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15264       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15265   {
15266     int move_direction, nextx, nexty;
15267
15268     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15269       MovDir[dropx][dropy] = drop_direction;
15270
15271     move_direction = MovDir[dropx][dropy];
15272     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15273     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15274
15275     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15276
15277 #if USE_FIX_IMPACT_COLLISION
15278     /* do not cause impact style collision by dropping elements that can fall */
15279     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15280 #else
15281     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15282 #endif
15283   }
15284
15285   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15286   player->is_dropping = TRUE;
15287
15288   player->drop_pressed_delay = 0;
15289   player->is_dropping_pressed = FALSE;
15290
15291   player->drop_x = dropx;
15292   player->drop_y = dropy;
15293
15294   return TRUE;
15295 }
15296
15297 /* ------------------------------------------------------------------------- */
15298 /* game sound playing functions                                              */
15299 /* ------------------------------------------------------------------------- */
15300
15301 static int *loop_sound_frame = NULL;
15302 static int *loop_sound_volume = NULL;
15303
15304 void InitPlayLevelSound()
15305 {
15306   int num_sounds = getSoundListSize();
15307
15308   checked_free(loop_sound_frame);
15309   checked_free(loop_sound_volume);
15310
15311   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15312   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15313 }
15314
15315 static void PlayLevelSound(int x, int y, int nr)
15316 {
15317   int sx = SCREENX(x), sy = SCREENY(y);
15318   int volume, stereo_position;
15319   int max_distance = 8;
15320   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15321
15322   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15323       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15324     return;
15325
15326   if (!IN_LEV_FIELD(x, y) ||
15327       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15328       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15329     return;
15330
15331   volume = SOUND_MAX_VOLUME;
15332
15333   if (!IN_SCR_FIELD(sx, sy))
15334   {
15335     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15336     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15337
15338     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15339   }
15340
15341   stereo_position = (SOUND_MAX_LEFT +
15342                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15343                      (SCR_FIELDX + 2 * max_distance));
15344
15345   if (IS_LOOP_SOUND(nr))
15346   {
15347     /* This assures that quieter loop sounds do not overwrite louder ones,
15348        while restarting sound volume comparison with each new game frame. */
15349
15350     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15351       return;
15352
15353     loop_sound_volume[nr] = volume;
15354     loop_sound_frame[nr] = FrameCounter;
15355   }
15356
15357   PlaySoundExt(nr, volume, stereo_position, type);
15358 }
15359
15360 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15361 {
15362   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15363                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15364                  y < LEVELY(BY1) ? LEVELY(BY1) :
15365                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15366                  sound_action);
15367 }
15368
15369 static void PlayLevelSoundAction(int x, int y, int action)
15370 {
15371   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15372 }
15373
15374 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15375 {
15376   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15377
15378   if (sound_effect != SND_UNDEFINED)
15379     PlayLevelSound(x, y, sound_effect);
15380 }
15381
15382 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15383                                               int action)
15384 {
15385   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15386
15387   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15388     PlayLevelSound(x, y, sound_effect);
15389 }
15390
15391 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15392 {
15393   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15394
15395   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15396     PlayLevelSound(x, y, sound_effect);
15397 }
15398
15399 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15400 {
15401   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15402
15403   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15404     StopSound(sound_effect);
15405 }
15406
15407 static void PlayLevelMusic()
15408 {
15409   if (levelset.music[level_nr] != MUS_UNDEFINED)
15410     PlayMusic(levelset.music[level_nr]);        /* from config file */
15411   else
15412     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15413 }
15414
15415 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15416 {
15417   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15418   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15419   int x = xx - 1 - offset;
15420   int y = yy - 1 - offset;
15421
15422   switch (sample)
15423   {
15424     case SAMPLE_blank:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15426       break;
15427
15428     case SAMPLE_roll:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15430       break;
15431
15432     case SAMPLE_stone:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15434       break;
15435
15436     case SAMPLE_nut:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15438       break;
15439
15440     case SAMPLE_crack:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15442       break;
15443
15444     case SAMPLE_bug:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15446       break;
15447
15448     case SAMPLE_tank:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15450       break;
15451
15452     case SAMPLE_android_clone:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15454       break;
15455
15456     case SAMPLE_android_move:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15458       break;
15459
15460     case SAMPLE_spring:
15461       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15462       break;
15463
15464     case SAMPLE_slurp:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15466       break;
15467
15468     case SAMPLE_eater:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15470       break;
15471
15472     case SAMPLE_eater_eat:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15474       break;
15475
15476     case SAMPLE_alien:
15477       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15478       break;
15479
15480     case SAMPLE_collect:
15481       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15482       break;
15483
15484     case SAMPLE_diamond:
15485       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15486       break;
15487
15488     case SAMPLE_squash:
15489       /* !!! CHECK THIS !!! */
15490 #if 1
15491       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15492 #else
15493       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15494 #endif
15495       break;
15496
15497     case SAMPLE_wonderfall:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15499       break;
15500
15501     case SAMPLE_drip:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15503       break;
15504
15505     case SAMPLE_push:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15507       break;
15508
15509     case SAMPLE_dirt:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15511       break;
15512
15513     case SAMPLE_acid:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15515       break;
15516
15517     case SAMPLE_ball:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15519       break;
15520
15521     case SAMPLE_grow:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15523       break;
15524
15525     case SAMPLE_wonder:
15526       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15527       break;
15528
15529     case SAMPLE_door:
15530       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15531       break;
15532
15533     case SAMPLE_exit_open:
15534       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15535       break;
15536
15537     case SAMPLE_exit_leave:
15538       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15539       break;
15540
15541     case SAMPLE_dynamite:
15542       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15543       break;
15544
15545     case SAMPLE_tick:
15546       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15547       break;
15548
15549     case SAMPLE_press:
15550       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15551       break;
15552
15553     case SAMPLE_wheel:
15554       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15555       break;
15556
15557     case SAMPLE_boom:
15558       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15559       break;
15560
15561     case SAMPLE_die:
15562       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15563       break;
15564
15565     case SAMPLE_time:
15566       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15567       break;
15568
15569     default:
15570       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15571       break;
15572   }
15573 }
15574
15575 #if 0
15576 void ChangeTime(int value)
15577 {
15578   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15579
15580   *time += value;
15581
15582   /* EMC game engine uses value from time counter of RND game engine */
15583   level.native_em_level->lev->time = *time;
15584
15585   DrawGameValue_Time(*time);
15586 }
15587
15588 void RaiseScore(int value)
15589 {
15590   /* EMC game engine and RND game engine have separate score counters */
15591   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15592                 &level.native_em_level->lev->score : &local_player->score);
15593
15594   *score += value;
15595
15596   DrawGameValue_Score(*score);
15597 }
15598 #endif
15599
15600 void RaiseScore(int value)
15601 {
15602   local_player->score += value;
15603
15604 #if 1
15605   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15606
15607   DisplayGameControlValues();
15608 #else
15609   DrawGameValue_Score(local_player->score);
15610 #endif
15611 }
15612
15613 void RaiseScoreElement(int element)
15614 {
15615   switch (element)
15616   {
15617     case EL_EMERALD:
15618     case EL_BD_DIAMOND:
15619     case EL_EMERALD_YELLOW:
15620     case EL_EMERALD_RED:
15621     case EL_EMERALD_PURPLE:
15622     case EL_SP_INFOTRON:
15623       RaiseScore(level.score[SC_EMERALD]);
15624       break;
15625     case EL_DIAMOND:
15626       RaiseScore(level.score[SC_DIAMOND]);
15627       break;
15628     case EL_CRYSTAL:
15629       RaiseScore(level.score[SC_CRYSTAL]);
15630       break;
15631     case EL_PEARL:
15632       RaiseScore(level.score[SC_PEARL]);
15633       break;
15634     case EL_BUG:
15635     case EL_BD_BUTTERFLY:
15636     case EL_SP_ELECTRON:
15637       RaiseScore(level.score[SC_BUG]);
15638       break;
15639     case EL_SPACESHIP:
15640     case EL_BD_FIREFLY:
15641     case EL_SP_SNIKSNAK:
15642       RaiseScore(level.score[SC_SPACESHIP]);
15643       break;
15644     case EL_YAMYAM:
15645     case EL_DARK_YAMYAM:
15646       RaiseScore(level.score[SC_YAMYAM]);
15647       break;
15648     case EL_ROBOT:
15649       RaiseScore(level.score[SC_ROBOT]);
15650       break;
15651     case EL_PACMAN:
15652       RaiseScore(level.score[SC_PACMAN]);
15653       break;
15654     case EL_NUT:
15655       RaiseScore(level.score[SC_NUT]);
15656       break;
15657     case EL_DYNAMITE:
15658     case EL_EM_DYNAMITE:
15659     case EL_SP_DISK_RED:
15660     case EL_DYNABOMB_INCREASE_NUMBER:
15661     case EL_DYNABOMB_INCREASE_SIZE:
15662     case EL_DYNABOMB_INCREASE_POWER:
15663       RaiseScore(level.score[SC_DYNAMITE]);
15664       break;
15665     case EL_SHIELD_NORMAL:
15666     case EL_SHIELD_DEADLY:
15667       RaiseScore(level.score[SC_SHIELD]);
15668       break;
15669     case EL_EXTRA_TIME:
15670       RaiseScore(level.extra_time_score);
15671       break;
15672     case EL_KEY_1:
15673     case EL_KEY_2:
15674     case EL_KEY_3:
15675     case EL_KEY_4:
15676     case EL_EM_KEY_1:
15677     case EL_EM_KEY_2:
15678     case EL_EM_KEY_3:
15679     case EL_EM_KEY_4:
15680     case EL_EMC_KEY_5:
15681     case EL_EMC_KEY_6:
15682     case EL_EMC_KEY_7:
15683     case EL_EMC_KEY_8:
15684     case EL_DC_KEY_WHITE:
15685       RaiseScore(level.score[SC_KEY]);
15686       break;
15687     default:
15688       RaiseScore(element_info[element].collect_score);
15689       break;
15690   }
15691 }
15692
15693 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15694 {
15695   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15696   {
15697 #if defined(NETWORK_AVALIABLE)
15698     if (options.network)
15699       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15700     else
15701 #endif
15702     {
15703       if (quick_quit)
15704       {
15705 #if 1
15706
15707 #if 1
15708         FadeSkipNextFadeIn();
15709 #else
15710         fading = fading_none;
15711 #endif
15712
15713 #else
15714         OpenDoor(DOOR_CLOSE_1);
15715 #endif
15716
15717         game_status = GAME_MODE_MAIN;
15718
15719 #if 1
15720         DrawAndFadeInMainMenu(REDRAW_FIELD);
15721 #else
15722         DrawMainMenu();
15723 #endif
15724       }
15725       else
15726       {
15727 #if 0
15728         FadeOut(REDRAW_FIELD);
15729 #endif
15730
15731         game_status = GAME_MODE_MAIN;
15732
15733         DrawAndFadeInMainMenu(REDRAW_FIELD);
15734       }
15735     }
15736   }
15737   else          /* continue playing the game */
15738   {
15739     if (tape.playing && tape.deactivate_display)
15740       TapeDeactivateDisplayOff(TRUE);
15741
15742     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15743
15744     if (tape.playing && tape.deactivate_display)
15745       TapeDeactivateDisplayOn();
15746   }
15747 }
15748
15749 void RequestQuitGame(boolean ask_if_really_quit)
15750 {
15751   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15752   boolean skip_request = AllPlayersGone || quick_quit;
15753
15754   RequestQuitGameExt(skip_request, quick_quit,
15755                      "Do you really want to quit the game ?");
15756 }
15757
15758
15759 /* ------------------------------------------------------------------------- */
15760 /* random generator functions                                                */
15761 /* ------------------------------------------------------------------------- */
15762
15763 unsigned int InitEngineRandom_RND(long seed)
15764 {
15765   game.num_random_calls = 0;
15766
15767 #if 0
15768   unsigned int rnd_seed = InitEngineRandom(seed);
15769
15770   printf("::: START RND: %d\n", rnd_seed);
15771
15772   return rnd_seed;
15773 #else
15774
15775   return InitEngineRandom(seed);
15776
15777 #endif
15778
15779 }
15780
15781 unsigned int RND(int max)
15782 {
15783   if (max > 0)
15784   {
15785     game.num_random_calls++;
15786
15787     return GetEngineRandom(max);
15788   }
15789
15790   return 0;
15791 }
15792
15793
15794 /* ------------------------------------------------------------------------- */
15795 /* game engine snapshot handling functions                                   */
15796 /* ------------------------------------------------------------------------- */
15797
15798 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15799
15800 struct EngineSnapshotInfo
15801 {
15802   /* runtime values for custom element collect score */
15803   int collect_score[NUM_CUSTOM_ELEMENTS];
15804
15805   /* runtime values for group element choice position */
15806   int choice_pos[NUM_GROUP_ELEMENTS];
15807
15808   /* runtime values for belt position animations */
15809   int belt_graphic[4 * NUM_BELT_PARTS];
15810   int belt_anim_mode[4 * NUM_BELT_PARTS];
15811 };
15812
15813 struct EngineSnapshotNodeInfo
15814 {
15815   void *buffer_orig;
15816   void *buffer_copy;
15817   int size;
15818 };
15819
15820 static struct EngineSnapshotInfo engine_snapshot_rnd;
15821 static ListNode *engine_snapshot_list = NULL;
15822 static char *snapshot_level_identifier = NULL;
15823 static int snapshot_level_nr = -1;
15824
15825 void FreeEngineSnapshot()
15826 {
15827   while (engine_snapshot_list != NULL)
15828     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15829                        checked_free);
15830
15831   setString(&snapshot_level_identifier, NULL);
15832   snapshot_level_nr = -1;
15833 }
15834
15835 static void SaveEngineSnapshotValues_RND()
15836 {
15837   static int belt_base_active_element[4] =
15838   {
15839     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15840     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15841     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15842     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15843   };
15844   int i, j;
15845
15846   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15847   {
15848     int element = EL_CUSTOM_START + i;
15849
15850     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15851   }
15852
15853   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15854   {
15855     int element = EL_GROUP_START + i;
15856
15857     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15858   }
15859
15860   for (i = 0; i < 4; i++)
15861   {
15862     for (j = 0; j < NUM_BELT_PARTS; j++)
15863     {
15864       int element = belt_base_active_element[i] + j;
15865       int graphic = el2img(element);
15866       int anim_mode = graphic_info[graphic].anim_mode;
15867
15868       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15869       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15870     }
15871   }
15872 }
15873
15874 static void LoadEngineSnapshotValues_RND()
15875 {
15876   unsigned long num_random_calls = game.num_random_calls;
15877   int i, j;
15878
15879   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15880   {
15881     int element = EL_CUSTOM_START + i;
15882
15883     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15884   }
15885
15886   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15887   {
15888     int element = EL_GROUP_START + i;
15889
15890     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15891   }
15892
15893   for (i = 0; i < 4; i++)
15894   {
15895     for (j = 0; j < NUM_BELT_PARTS; j++)
15896     {
15897       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15898       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15899
15900       graphic_info[graphic].anim_mode = anim_mode;
15901     }
15902   }
15903
15904   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15905   {
15906     InitRND(tape.random_seed);
15907     for (i = 0; i < num_random_calls; i++)
15908       RND(1);
15909   }
15910
15911   if (game.num_random_calls != num_random_calls)
15912   {
15913     Error(ERR_INFO, "number of random calls out of sync");
15914     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15915     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15916     Error(ERR_EXIT, "this should not happen -- please debug");
15917   }
15918 }
15919
15920 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15921 {
15922   struct EngineSnapshotNodeInfo *bi =
15923     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15924
15925   bi->buffer_orig = buffer;
15926   bi->buffer_copy = checked_malloc(size);
15927   bi->size = size;
15928
15929   memcpy(bi->buffer_copy, buffer, size);
15930
15931   addNodeToList(&engine_snapshot_list, NULL, bi);
15932 }
15933
15934 void SaveEngineSnapshot()
15935 {
15936   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15937
15938   if (level_editor_test_game)   /* do not save snapshots from editor */
15939     return;
15940
15941   /* copy some special values to a structure better suited for the snapshot */
15942
15943   SaveEngineSnapshotValues_RND();
15944   SaveEngineSnapshotValues_EM();
15945
15946   /* save values stored in special snapshot structure */
15947
15948   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15949   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15950
15951   /* save further RND engine values */
15952
15953   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15954   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15955   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15956
15957   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15959   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15960   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15961
15962   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15963   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15964   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15966   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15967
15968   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15971
15972   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15973
15974   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15975
15976   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15977   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15978
15979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15981   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15983   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15988   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15996   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15997
15998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16000
16001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16003   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16004
16005   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16007
16008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16009   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16010   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16012   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16013
16014   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16016
16017   /* save level identification information */
16018
16019   setString(&snapshot_level_identifier, leveldir_current->identifier);
16020   snapshot_level_nr = level_nr;
16021
16022 #if 0
16023   ListNode *node = engine_snapshot_list;
16024   int num_bytes = 0;
16025
16026   while (node != NULL)
16027   {
16028     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16029
16030     node = node->next;
16031   }
16032
16033   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16034 #endif
16035 }
16036
16037 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16038 {
16039   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16040 }
16041
16042 void LoadEngineSnapshot()
16043 {
16044   ListNode *node = engine_snapshot_list;
16045
16046   if (engine_snapshot_list == NULL)
16047     return;
16048
16049   while (node != NULL)
16050   {
16051     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16052
16053     node = node->next;
16054   }
16055
16056   /* restore special values from snapshot structure */
16057
16058   LoadEngineSnapshotValues_RND();
16059   LoadEngineSnapshotValues_EM();
16060 }
16061
16062 boolean CheckEngineSnapshot()
16063 {
16064   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16065           snapshot_level_nr == level_nr);
16066 }
16067
16068
16069 /* ---------- new game button stuff ---------------------------------------- */
16070
16071 /* graphic position values for game buttons */
16072 #define GAME_BUTTON_XSIZE       30
16073 #define GAME_BUTTON_YSIZE       30
16074 #define GAME_BUTTON_XPOS        5
16075 #define GAME_BUTTON_YPOS        215
16076 #define SOUND_BUTTON_XPOS       5
16077 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16078
16079 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16080 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16081 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16082 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16083 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16084 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16085
16086 static struct
16087 {
16088   int *x, *y;
16089   int gd_x, gd_y;
16090   int gadget_id;
16091   char *infotext;
16092 } gamebutton_info[NUM_GAME_BUTTONS] =
16093 {
16094 #if 1
16095   {
16096     &game.button.stop.x,        &game.button.stop.y,
16097     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16098     GAME_CTRL_ID_STOP,
16099     "stop game"
16100   },
16101   {
16102     &game.button.pause.x,       &game.button.pause.y,
16103     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16104     GAME_CTRL_ID_PAUSE,
16105     "pause game"
16106   },
16107   {
16108     &game.button.play.x,        &game.button.play.y,
16109     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16110     GAME_CTRL_ID_PLAY,
16111     "play game"
16112   },
16113   {
16114     &game.button.sound_music.x, &game.button.sound_music.y,
16115     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16116     SOUND_CTRL_ID_MUSIC,
16117     "background music on/off"
16118   },
16119   {
16120     &game.button.sound_loops.x, &game.button.sound_loops.y,
16121     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16122     SOUND_CTRL_ID_LOOPS,
16123     "sound loops on/off"
16124   },
16125   {
16126     &game.button.sound_simple.x,&game.button.sound_simple.y,
16127     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16128     SOUND_CTRL_ID_SIMPLE,
16129     "normal sounds on/off"
16130   }
16131 #else
16132   {
16133     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16134     GAME_CTRL_ID_STOP,
16135     "stop game"
16136   },
16137   {
16138     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16139     GAME_CTRL_ID_PAUSE,
16140     "pause game"
16141   },
16142   {
16143     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16144     GAME_CTRL_ID_PLAY,
16145     "play game"
16146   },
16147   {
16148     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16149     SOUND_CTRL_ID_MUSIC,
16150     "background music on/off"
16151   },
16152   {
16153     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16154     SOUND_CTRL_ID_LOOPS,
16155     "sound loops on/off"
16156   },
16157   {
16158     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16159     SOUND_CTRL_ID_SIMPLE,
16160     "normal sounds on/off"
16161   }
16162 #endif
16163 };
16164
16165 void CreateGameButtons()
16166 {
16167   int i;
16168
16169   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16170   {
16171     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16172     struct GadgetInfo *gi;
16173     int button_type;
16174     boolean checked;
16175     unsigned long event_mask;
16176     int x, y;
16177     int gd_xoffset, gd_yoffset;
16178     int gd_x1, gd_x2, gd_y1, gd_y2;
16179     int id = i;
16180
16181     x = DX + *gamebutton_info[i].x;
16182     y = DY + *gamebutton_info[i].y;
16183     gd_xoffset = gamebutton_info[i].gd_x;
16184     gd_yoffset = gamebutton_info[i].gd_y;
16185     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16186     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16187
16188     if (id == GAME_CTRL_ID_STOP ||
16189         id == GAME_CTRL_ID_PAUSE ||
16190         id == GAME_CTRL_ID_PLAY)
16191     {
16192       button_type = GD_TYPE_NORMAL_BUTTON;
16193       checked = FALSE;
16194       event_mask = GD_EVENT_RELEASED;
16195       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16196       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16197     }
16198     else
16199     {
16200       button_type = GD_TYPE_CHECK_BUTTON;
16201       checked =
16202         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16203          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16204          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16205       event_mask = GD_EVENT_PRESSED;
16206       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16207       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16208     }
16209
16210     gi = CreateGadget(GDI_CUSTOM_ID, id,
16211                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16212 #if 1
16213                       GDI_X, x,
16214                       GDI_Y, y,
16215 #else
16216                       GDI_X, DX + gd_xoffset,
16217                       GDI_Y, DY + gd_yoffset,
16218 #endif
16219                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16220                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16221                       GDI_TYPE, button_type,
16222                       GDI_STATE, GD_BUTTON_UNPRESSED,
16223                       GDI_CHECKED, checked,
16224                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16225                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16226                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16227                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16228                       GDI_DIRECT_DRAW, FALSE,
16229                       GDI_EVENT_MASK, event_mask,
16230                       GDI_CALLBACK_ACTION, HandleGameButtons,
16231                       GDI_END);
16232
16233     if (gi == NULL)
16234       Error(ERR_EXIT, "cannot create gadget");
16235
16236     game_gadget[id] = gi;
16237   }
16238 }
16239
16240 void FreeGameButtons()
16241 {
16242   int i;
16243
16244   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16245     FreeGadget(game_gadget[i]);
16246 }
16247
16248 static void MapGameButtons()
16249 {
16250   int i;
16251
16252   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16253     MapGadget(game_gadget[i]);
16254 }
16255
16256 void UnmapGameButtons()
16257 {
16258   int i;
16259
16260   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16261     UnmapGadget(game_gadget[i]);
16262 }
16263
16264 void RedrawGameButtons()
16265 {
16266   int i;
16267
16268   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16269     RedrawGadget(game_gadget[i]);
16270 }
16271
16272 static void HandleGameButtons(struct GadgetInfo *gi)
16273 {
16274   int id = gi->custom_id;
16275
16276   if (game_status != GAME_MODE_PLAYING)
16277     return;
16278
16279   switch (id)
16280   {
16281     case GAME_CTRL_ID_STOP:
16282       if (tape.playing)
16283         TapeStop();
16284       else
16285         RequestQuitGame(TRUE);
16286       break;
16287
16288     case GAME_CTRL_ID_PAUSE:
16289       if (options.network)
16290       {
16291 #if defined(NETWORK_AVALIABLE)
16292         if (tape.pausing)
16293           SendToServer_ContinuePlaying();
16294         else
16295           SendToServer_PausePlaying();
16296 #endif
16297       }
16298       else
16299         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16300       break;
16301
16302     case GAME_CTRL_ID_PLAY:
16303       if (tape.pausing)
16304       {
16305 #if defined(NETWORK_AVALIABLE)
16306         if (options.network)
16307           SendToServer_ContinuePlaying();
16308         else
16309 #endif
16310         {
16311           tape.pausing = FALSE;
16312           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16313         }
16314       }
16315       break;
16316
16317     case SOUND_CTRL_ID_MUSIC:
16318       if (setup.sound_music)
16319       { 
16320         setup.sound_music = FALSE;
16321         FadeMusic();
16322       }
16323       else if (audio.music_available)
16324       { 
16325         setup.sound = setup.sound_music = TRUE;
16326
16327         SetAudioMode(setup.sound);
16328
16329         PlayLevelMusic();
16330       }
16331       break;
16332
16333     case SOUND_CTRL_ID_LOOPS:
16334       if (setup.sound_loops)
16335         setup.sound_loops = FALSE;
16336       else if (audio.loops_available)
16337       {
16338         setup.sound = setup.sound_loops = TRUE;
16339         SetAudioMode(setup.sound);
16340       }
16341       break;
16342
16343     case SOUND_CTRL_ID_SIMPLE:
16344       if (setup.sound_simple)
16345         setup.sound_simple = FALSE;
16346       else if (audio.sound_available)
16347       {
16348         setup.sound = setup.sound_simple = TRUE;
16349         SetAudioMode(setup.sound);
16350       }
16351       break;
16352
16353     default:
16354       break;
16355   }
16356 }