rnd-20100202-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active &&
4232           !tape.player_participates[i])
4233       {
4234         struct PlayerInfo *player = &stored_player[i];
4235         int jx = player->jx, jy = player->jy;
4236
4237         player->active = FALSE;
4238         StorePlayer[jx][jy] = 0;
4239         Feld[jx][jy] = EL_EMPTY;
4240       }
4241     }
4242 #endif
4243   }
4244   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4245   {
4246     /* when in single player mode, eliminate all but the first active player */
4247
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249     {
4250       if (stored_player[i].active)
4251       {
4252         for (j = i + 1; j < MAX_PLAYERS; j++)
4253         {
4254           if (stored_player[j].active)
4255           {
4256             struct PlayerInfo *player = &stored_player[j];
4257             int jx = player->jx, jy = player->jy;
4258
4259             player->active = FALSE;
4260             player->present = FALSE;
4261
4262             StorePlayer[jx][jy] = 0;
4263             Feld[jx][jy] = EL_EMPTY;
4264           }
4265         }
4266       }
4267     }
4268   }
4269
4270   /* when recording the game, store which players take part in the game */
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284   if (options.debug)
4285   {
4286     for (i = 0; i < MAX_PLAYERS; i++)
4287     {
4288       struct PlayerInfo *player = &stored_player[i];
4289
4290       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4291              i+1,
4292              player->present,
4293              player->connected,
4294              player->active);
4295       if (local_player == player)
4296         printf("Player  %d is local player.\n", i+1);
4297     }
4298   }
4299
4300   if (BorderElement == EL_EMPTY)
4301   {
4302     SBX_Left = 0;
4303     SBX_Right = lev_fieldx - SCR_FIELDX;
4304     SBY_Upper = 0;
4305     SBY_Lower = lev_fieldy - SCR_FIELDY;
4306   }
4307   else
4308   {
4309     SBX_Left = -1;
4310     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311     SBY_Upper = -1;
4312     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4313   }
4314
4315   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320
4321   /* if local player not found, look for custom element that might create
4322      the player (make some assumptions about the right custom element) */
4323   if (!local_player->present)
4324   {
4325     int start_x = 0, start_y = 0;
4326     int found_rating = 0;
4327     int found_element = EL_UNDEFINED;
4328     int player_nr = local_player->index_nr;
4329
4330     SCAN_PLAYFIELD(x, y)
4331     {
4332       int element = Feld[x][y];
4333       int content;
4334       int xx, yy;
4335       boolean is_player;
4336
4337       if (level.use_start_element[player_nr] &&
4338           level.start_element[player_nr] == element &&
4339           found_rating < 4)
4340       {
4341         start_x = x;
4342         start_y = y;
4343
4344         found_rating = 4;
4345         found_element = element;
4346       }
4347
4348       if (!IS_CUSTOM_ELEMENT(element))
4349         continue;
4350
4351       if (CAN_CHANGE(element))
4352       {
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as single target */
4356           content = element_info[element].change_page[i].target_element;
4357           is_player = ELEM_IS_PLAYER(content);
4358
4359           if (is_player && (found_rating < 3 ||
4360                             (found_rating == 3 && element < found_element)))
4361           {
4362             start_x = x;
4363             start_y = y;
4364
4365             found_rating = 3;
4366             found_element = element;
4367           }
4368         }
4369       }
4370
4371       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372       {
4373         /* check for player created from custom element as explosion content */
4374         content = element_info[element].content.e[xx][yy];
4375         is_player = ELEM_IS_PLAYER(content);
4376
4377         if (is_player && (found_rating < 2 ||
4378                           (found_rating == 2 && element < found_element)))
4379         {
4380           start_x = x + xx - 1;
4381           start_y = y + yy - 1;
4382
4383           found_rating = 2;
4384           found_element = element;
4385         }
4386
4387         if (!CAN_CHANGE(element))
4388           continue;
4389
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           /* check for player created from custom element as extended target */
4393           content =
4394             element_info[element].change_page[i].target_content.e[xx][yy];
4395
4396           is_player = ELEM_IS_PLAYER(content);
4397
4398           if (is_player && (found_rating < 1 ||
4399                             (found_rating == 1 && element < found_element)))
4400           {
4401             start_x = x + xx - 1;
4402             start_y = y + yy - 1;
4403
4404             found_rating = 1;
4405             found_element = element;
4406           }
4407         }
4408       }
4409     }
4410
4411     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4412                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4413                 start_x - MIDPOSX);
4414
4415     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4417                 start_y - MIDPOSY);
4418   }
4419   else
4420   {
4421     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4422                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423                 local_player->jx - MIDPOSX);
4424
4425     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427                 local_player->jy - MIDPOSY);
4428   }
4429
4430 #if 0
4431   /* do not use PLAYING mask for fading out from main screen */
4432   game_status = GAME_MODE_MAIN;
4433 #endif
4434
4435   StopAnimation();
4436
4437   if (!game.restart_level)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440 #if 1
4441   if (level_editor_test_game)
4442     FadeSkipNextFadeIn();
4443   else
4444     FadeSetEnterScreen();
4445 #else
4446   if (level_editor_test_game)
4447     fading = fading_none;
4448   else
4449     fading = menu.destination;
4450 #endif
4451
4452 #if 1
4453   FadeOut(REDRAW_FIELD);
4454 #else
4455   if (do_fading)
4456     FadeOut(REDRAW_FIELD);
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4472   {
4473     InitGameEngine_SP();
4474
4475     /* blit playfield from scroll buffer to normal back buffer for fading in */
4476     BlitScreenToBitmap_SP(backbuffer);
4477   }
4478   else
4479   {
4480     DrawLevel();
4481     DrawAllPlayers();
4482
4483     /* after drawing the level, correct some elements */
4484     if (game.timegate_time_left == 0)
4485       CloseAllOpenTimegates();
4486
4487     /* blit playfield from scroll buffer to normal back buffer for fading in */
4488     if (setup.soft_scrolling)
4489       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4490
4491     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4492   }
4493   /* !!! FIX THIS (END) !!! */
4494
4495 #if 1
4496   FadeIn(REDRAW_FIELD);
4497 #else
4498   if (do_fading)
4499     FadeIn(REDRAW_FIELD);
4500
4501   BackToFront();
4502 #endif
4503
4504   if (!game.restart_level)
4505   {
4506     /* copy default game door content to main double buffer */
4507     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4509   }
4510
4511   SetPanelBackground();
4512   SetDrawBackgroundMask(REDRAW_DOOR_1);
4513
4514 #if 1
4515   UpdateAndDisplayGameControlValues();
4516 #else
4517   UpdateGameDoorValues();
4518   DrawGameDoorValues();
4519 #endif
4520
4521   if (!game.restart_level)
4522   {
4523     UnmapGameButtons();
4524     UnmapTapeButtons();
4525     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4528     MapGameButtons();
4529     MapTapeButtons();
4530
4531     /* copy actual game door content to door double buffer for OpenDoor() */
4532     BlitBitmap(drawto, bitmap_db_door,
4533                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4534
4535     OpenDoor(DOOR_OPEN_ALL);
4536
4537     PlaySound(SND_GAME_STARTING);
4538
4539     if (setup.sound_music)
4540       PlayLevelMusic();
4541
4542     KeyboardAutoRepeatOffUnlessAutoplay();
4543
4544     if (options.debug)
4545     {
4546       for (i = 0; i < MAX_PLAYERS; i++)
4547         printf("Player %d %sactive.\n",
4548                i + 1, (stored_player[i].active ? "" : "not "));
4549     }
4550   }
4551
4552 #if 1
4553   UnmapAllGadgets();
4554
4555   MapGameButtons();
4556   MapTapeButtons();
4557 #endif
4558
4559   game.restart_level = FALSE;
4560 }
4561
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4563 {
4564   /* this is used for non-R'n'D game engines to update certain engine values */
4565
4566   /* needed to determine if sounds are played within the visible screen area */
4567   scroll_x = actual_scroll_x;
4568   scroll_y = actual_scroll_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Feld[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Feld[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Feld[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Feld[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Feld[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Feld[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Feld[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Feld[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     default:
4655       if (IS_CUSTOM_ELEMENT(element))
4656       {
4657         struct ElementInfo *ei = &element_info[element];
4658         int move_direction_initial = ei->move_direction_initial;
4659         int move_pattern = ei->move_pattern;
4660
4661         if (move_direction_initial == MV_START_PREVIOUS)
4662         {
4663           if (MovDir[x][y] != MV_NONE)
4664             return;
4665
4666           move_direction_initial = MV_START_AUTOMATIC;
4667         }
4668
4669         if (move_direction_initial == MV_START_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_direction_initial & MV_ANY_DIRECTION)
4672           MovDir[x][y] = move_direction_initial;
4673         else if (move_pattern == MV_ALL_DIRECTIONS ||
4674                  move_pattern == MV_TURNING_LEFT ||
4675                  move_pattern == MV_TURNING_RIGHT ||
4676                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4677                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4678                  move_pattern == MV_TURNING_RANDOM)
4679           MovDir[x][y] = 1 << RND(4);
4680         else if (move_pattern == MV_HORIZONTAL)
4681           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682         else if (move_pattern == MV_VERTICAL)
4683           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684         else if (move_pattern & MV_ANY_DIRECTION)
4685           MovDir[x][y] = element_info[element].move_pattern;
4686         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687                  move_pattern == MV_ALONG_RIGHT_SIDE)
4688         {
4689           /* use random direction as default start direction */
4690           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691             MovDir[x][y] = 1 << RND(4);
4692
4693           for (i = 0; i < NUM_DIRECTIONS; i++)
4694           {
4695             int x1 = x + xy[i][0];
4696             int y1 = y + xy[i][1];
4697
4698             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4699             {
4700               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701                 MovDir[x][y] = direction[0][i];
4702               else
4703                 MovDir[x][y] = direction[1][i];
4704
4705               break;
4706             }
4707           }
4708         }                
4709       }
4710       else
4711       {
4712         MovDir[x][y] = 1 << RND(4);
4713
4714         if (element != EL_BUG &&
4715             element != EL_SPACESHIP &&
4716             element != EL_BD_BUTTERFLY &&
4717             element != EL_BD_FIREFLY)
4718           break;
4719
4720         for (i = 0; i < NUM_DIRECTIONS; i++)
4721         {
4722           int x1 = x + xy[i][0];
4723           int y1 = y + xy[i][1];
4724
4725           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4726           {
4727             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4728             {
4729               MovDir[x][y] = direction[0][i];
4730               break;
4731             }
4732             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4734             {
4735               MovDir[x][y] = direction[1][i];
4736               break;
4737             }
4738           }
4739         }
4740       }
4741       break;
4742   }
4743
4744   GfxDir[x][y] = MovDir[x][y];
4745 }
4746
4747 void InitAmoebaNr(int x, int y)
4748 {
4749   int i;
4750   int group_nr = AmoebeNachbarNr(x, y);
4751
4752   if (group_nr == 0)
4753   {
4754     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4755     {
4756       if (AmoebaCnt[i] == 0)
4757       {
4758         group_nr = i;
4759         break;
4760       }
4761     }
4762   }
4763
4764   AmoebaNr[x][y] = group_nr;
4765   AmoebaCnt[group_nr]++;
4766   AmoebaCnt2[group_nr]++;
4767 }
4768
4769 static void PlayerWins(struct PlayerInfo *player)
4770 {
4771   player->LevelSolved = TRUE;
4772   player->GameOver = TRUE;
4773
4774   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775                          level.native_em_level->lev->score : player->score);
4776
4777   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778   player->LevelSolved_CountingScore = player->score_final;
4779 }
4780
4781 void GameWon()
4782 {
4783   static int time, time_final;
4784   static int score, score_final;
4785   static int game_over_delay_1 = 0;
4786   static int game_over_delay_2 = 0;
4787   int game_over_delay_value_1 = 50;
4788   int game_over_delay_value_2 = 50;
4789
4790   if (!local_player->LevelSolved_GameWon)
4791   {
4792     int i;
4793
4794     /* do not start end game actions before the player stops moving (to exit) */
4795     if (local_player->MovPos)
4796       return;
4797
4798     local_player->LevelSolved_GameWon = TRUE;
4799     local_player->LevelSolved_SaveTape = tape.recording;
4800     local_player->LevelSolved_SaveScore = !tape.playing;
4801
4802     if (tape.auto_play)         /* tape might already be stopped here */
4803       tape.auto_play_level_solved = TRUE;
4804
4805 #if 1
4806     TapeStop();
4807 #endif
4808
4809     game_over_delay_1 = game_over_delay_value_1;
4810     game_over_delay_2 = game_over_delay_value_2;
4811
4812     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813     score = score_final = local_player->score_final;
4814
4815     if (TimeLeft > 0)
4816     {
4817       time_final = 0;
4818       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4819     }
4820     else if (level.time == 0 && TimePlayed < 999)
4821     {
4822       time_final = 999;
4823       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4824     }
4825
4826     local_player->score_final = score_final;
4827
4828     if (level_editor_test_game)
4829     {
4830       time = time_final;
4831       score = score_final;
4832
4833 #if 1
4834       local_player->LevelSolved_CountingTime = time;
4835       local_player->LevelSolved_CountingScore = score;
4836
4837       game_panel_controls[GAME_PANEL_TIME].value = time;
4838       game_panel_controls[GAME_PANEL_SCORE].value = score;
4839
4840       DisplayGameControlValues();
4841 #else
4842       DrawGameValue_Time(time);
4843       DrawGameValue_Score(score);
4844 #endif
4845     }
4846
4847     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4848     {
4849       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4850       {
4851         /* close exit door after last player */
4852         if ((AllPlayersGone &&
4853              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4858         {
4859           int element = Feld[ExitX][ExitY];
4860
4861 #if 0
4862           if (element == EL_EM_EXIT_OPEN ||
4863               element == EL_EM_STEEL_EXIT_OPEN)
4864           {
4865             Bang(ExitX, ExitY);
4866           }
4867           else
4868 #endif
4869           {
4870             Feld[ExitX][ExitY] =
4871               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4872                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4873                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4874                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4875                EL_EM_STEEL_EXIT_CLOSING);
4876
4877             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4878           }
4879         }
4880
4881         /* player disappears */
4882         DrawLevelField(ExitX, ExitY);
4883       }
4884
4885       for (i = 0; i < MAX_PLAYERS; i++)
4886       {
4887         struct PlayerInfo *player = &stored_player[i];
4888
4889         if (player->present)
4890         {
4891           RemovePlayer(player);
4892
4893           /* player disappears */
4894           DrawLevelField(player->jx, player->jy);
4895         }
4896       }
4897     }
4898
4899     PlaySound(SND_GAME_WINNING);
4900   }
4901
4902   if (game_over_delay_1 > 0)
4903   {
4904     game_over_delay_1--;
4905
4906     return;
4907   }
4908
4909   if (time != time_final)
4910   {
4911     int time_to_go = ABS(time_final - time);
4912     int time_count_dir = (time < time_final ? +1 : -1);
4913     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4914
4915     time  += time_count_steps * time_count_dir;
4916     score += time_count_steps * level.score[SC_TIME_BONUS];
4917
4918 #if 1
4919     local_player->LevelSolved_CountingTime = time;
4920     local_player->LevelSolved_CountingScore = score;
4921
4922     game_panel_controls[GAME_PANEL_TIME].value = time;
4923     game_panel_controls[GAME_PANEL_SCORE].value = score;
4924
4925     DisplayGameControlValues();
4926 #else
4927     DrawGameValue_Time(time);
4928     DrawGameValue_Score(score);
4929 #endif
4930
4931     if (time == time_final)
4932       StopSound(SND_GAME_LEVELTIME_BONUS);
4933     else if (setup.sound_loops)
4934       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4935     else
4936       PlaySound(SND_GAME_LEVELTIME_BONUS);
4937
4938     return;
4939   }
4940
4941   local_player->LevelSolved_PanelOff = TRUE;
4942
4943   if (game_over_delay_2 > 0)
4944   {
4945     game_over_delay_2--;
4946
4947     return;
4948   }
4949
4950 #if 1
4951   GameEnd();
4952 #endif
4953 }
4954
4955 void GameEnd()
4956 {
4957   int hi_pos;
4958   boolean raise_level = FALSE;
4959
4960   local_player->LevelSolved_GameEnd = TRUE;
4961
4962   CloseDoor(DOOR_CLOSE_1);
4963
4964   if (local_player->LevelSolved_SaveTape)
4965   {
4966 #if 0
4967     TapeStop();
4968 #endif
4969
4970 #if 1
4971     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4972 #else
4973     SaveTape(tape.level_nr);            /* ask to save tape */
4974 #endif
4975   }
4976
4977   if (level_editor_test_game)
4978   {
4979     game_status = GAME_MODE_MAIN;
4980
4981 #if 1
4982     DrawAndFadeInMainMenu(REDRAW_FIELD);
4983 #else
4984     DrawMainMenu();
4985 #endif
4986
4987     return;
4988   }
4989
4990   if (!local_player->LevelSolved_SaveScore)
4991   {
4992 #if 1
4993     FadeOut(REDRAW_FIELD);
4994 #endif
4995
4996     game_status = GAME_MODE_MAIN;
4997
4998     DrawAndFadeInMainMenu(REDRAW_FIELD);
4999
5000     return;
5001   }
5002
5003   if (level_nr == leveldir_current->handicap_level)
5004   {
5005     leveldir_current->handicap_level++;
5006     SaveLevelSetup_SeriesInfo();
5007   }
5008
5009   if (level_nr < leveldir_current->last_level)
5010     raise_level = TRUE;                 /* advance to next level */
5011
5012   if ((hi_pos = NewHiScore()) >= 0) 
5013   {
5014     game_status = GAME_MODE_SCORES;
5015
5016     DrawHallOfFame(hi_pos);
5017
5018     if (raise_level)
5019     {
5020       level_nr++;
5021       TapeErase();
5022     }
5023   }
5024   else
5025   {
5026 #if 1
5027     FadeOut(REDRAW_FIELD);
5028 #endif
5029
5030     game_status = GAME_MODE_MAIN;
5031
5032     if (raise_level)
5033     {
5034       level_nr++;
5035       TapeErase();
5036     }
5037
5038     DrawAndFadeInMainMenu(REDRAW_FIELD);
5039   }
5040 }
5041
5042 int NewHiScore()
5043 {
5044   int k, l;
5045   int position = -1;
5046
5047   LoadScore(level_nr);
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5051     return -1;
5052
5053   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5054   {
5055     if (local_player->score_final > highscore[k].Score)
5056     {
5057       /* player has made it to the hall of fame */
5058
5059       if (k < MAX_SCORE_ENTRIES - 1)
5060       {
5061         int m = MAX_SCORE_ENTRIES - 1;
5062
5063 #ifdef ONE_PER_NAME
5064         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065           if (strEqual(setup.player_name, highscore[l].Name))
5066             m = l;
5067         if (m == k)     /* player's new highscore overwrites his old one */
5068           goto put_into_list;
5069 #endif
5070
5071         for (l = m; l > k; l--)
5072         {
5073           strcpy(highscore[l].Name, highscore[l - 1].Name);
5074           highscore[l].Score = highscore[l - 1].Score;
5075         }
5076       }
5077
5078 #ifdef ONE_PER_NAME
5079       put_into_list:
5080 #endif
5081       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083       highscore[k].Score = local_player->score_final; 
5084       position = k;
5085       break;
5086     }
5087
5088 #ifdef ONE_PER_NAME
5089     else if (!strncmp(setup.player_name, highscore[k].Name,
5090                       MAX_PLAYER_NAME_LEN))
5091       break;    /* player already there with a higher score */
5092 #endif
5093
5094   }
5095
5096   if (position >= 0) 
5097     SaveScore(level_nr);
5098
5099   return position;
5100 }
5101
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5103 {
5104   int element = Feld[x][y];
5105   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5107   int horiz_move = (dx != 0);
5108   int sign = (horiz_move ? dx : dy);
5109   int step = sign * element_info[element].move_stepsize;
5110
5111   /* special values for move stepsize for spring and things on conveyor belt */
5112   if (horiz_move)
5113   {
5114     if (CAN_FALL(element) &&
5115         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117     else if (element == EL_SPRING)
5118       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5119   }
5120
5121   return step;
5122 }
5123
5124 inline static int getElementMoveStepsize(int x, int y)
5125 {
5126   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5127 }
5128
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5130 {
5131   if (player->GfxAction != action || player->GfxDir != dir)
5132   {
5133 #if 0
5134     printf("Player frame reset! (%d => %d, %d => %d)\n",
5135            player->GfxAction, action, player->GfxDir, dir);
5136 #endif
5137
5138     player->GfxAction = action;
5139     player->GfxDir = dir;
5140     player->Frame = 0;
5141     player->StepFrame = 0;
5142   }
5143 }
5144
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5147 {
5148   int element = Feld[x][y];
5149   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150   int last_gfx_frame = GfxFrame[x][y];
5151
5152   if (graphic_info[graphic].anim_global_sync)
5153     GfxFrame[x][y] = FrameCounter;
5154   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155     GfxFrame[x][y] = CustomValue[x][y];
5156   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157     GfxFrame[x][y] = element_info[element].collect_score;
5158   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159     GfxFrame[x][y] = ChangeDelay[x][y];
5160
5161   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162     DrawLevelGraphicAnimation(x, y, graphic);
5163 }
5164 #endif
5165
5166 static void ResetGfxAnimation(int x, int y)
5167 {
5168   GfxAction[x][y] = ACTION_DEFAULT;
5169   GfxDir[x][y] = MovDir[x][y];
5170   GfxFrame[x][y] = 0;
5171
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173   ResetGfxFrame(x, y, FALSE);
5174 #endif
5175 }
5176
5177 static void ResetRandomAnimationValue(int x, int y)
5178 {
5179   GfxRandom[x][y] = INIT_GFX_RANDOM();
5180 }
5181
5182 void InitMovingField(int x, int y, int direction)
5183 {
5184   int element = Feld[x][y];
5185   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5187   int newx = x + dx;
5188   int newy = y + dy;
5189   boolean is_moving_before, is_moving_after;
5190 #if 0
5191   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5192 #endif
5193
5194   /* check if element was/is moving or being moved before/after mode change */
5195 #if 1
5196 #if 1
5197   is_moving_before = (WasJustMoving[x][y] != 0);
5198 #else
5199   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200   is_moving_before = WasJustMoving[x][y];
5201 #endif
5202 #else
5203   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5204 #endif
5205   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5206
5207   /* reset animation only for moving elements which change direction of moving
5208      or which just started or stopped moving
5209      (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5211 #if 1
5212   if (is_moving_before != is_moving_after ||
5213       direction != MovDir[x][y])
5214     ResetGfxAnimation(x, y);
5215 #else
5216   if ((is_moving_before || is_moving_after) && !continues_moving)
5217     ResetGfxAnimation(x, y);
5218 #endif
5219 #else
5220   if (!continues_moving)
5221     ResetGfxAnimation(x, y);
5222 #endif
5223
5224   MovDir[x][y] = direction;
5225   GfxDir[x][y] = direction;
5226
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229                      direction == MV_DOWN && CAN_FALL(element) ?
5230                      ACTION_FALLING : ACTION_MOVING);
5231 #else
5232   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233                      ACTION_FALLING : ACTION_MOVING);
5234 #endif
5235
5236   /* this is needed for CEs with property "can move" / "not moving" */
5237
5238   if (is_moving_after)
5239   {
5240     if (Feld[newx][newy] == EL_EMPTY)
5241       Feld[newx][newy] = EL_BLOCKED;
5242
5243     MovDir[newx][newy] = MovDir[x][y];
5244
5245 #if USE_NEW_CUSTOM_VALUE
5246     CustomValue[newx][newy] = CustomValue[x][y];
5247 #endif
5248
5249     GfxFrame[newx][newy] = GfxFrame[x][y];
5250     GfxRandom[newx][newy] = GfxRandom[x][y];
5251     GfxAction[newx][newy] = GfxAction[x][y];
5252     GfxDir[newx][newy] = GfxDir[x][y];
5253   }
5254 }
5255
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5257 {
5258   int direction = MovDir[x][y];
5259   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5261
5262   *goes_to_x = newx;
5263   *goes_to_y = newy;
5264 }
5265
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5267 {
5268   int oldx = x, oldy = y;
5269   int direction = MovDir[x][y];
5270
5271   if (direction == MV_LEFT)
5272     oldx++;
5273   else if (direction == MV_RIGHT)
5274     oldx--;
5275   else if (direction == MV_UP)
5276     oldy++;
5277   else if (direction == MV_DOWN)
5278     oldy--;
5279
5280   *comes_from_x = oldx;
5281   *comes_from_y = oldy;
5282 }
5283
5284 int MovingOrBlocked2Element(int x, int y)
5285 {
5286   int element = Feld[x][y];
5287
5288   if (element == EL_BLOCKED)
5289   {
5290     int oldx, oldy;
5291
5292     Blocked2Moving(x, y, &oldx, &oldy);
5293     return Feld[oldx][oldy];
5294   }
5295   else
5296     return element;
5297 }
5298
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5300 {
5301   /* like MovingOrBlocked2Element(), but if element is moving
5302      and (x,y) is the field the moving element is just leaving,
5303      return EL_BLOCKED instead of the element value */
5304   int element = Feld[x][y];
5305
5306   if (IS_MOVING(x, y))
5307   {
5308     if (element == EL_BLOCKED)
5309     {
5310       int oldx, oldy;
5311
5312       Blocked2Moving(x, y, &oldx, &oldy);
5313       return Feld[oldx][oldy];
5314     }
5315     else
5316       return EL_BLOCKED;
5317   }
5318   else
5319     return element;
5320 }
5321
5322 static void RemoveField(int x, int y)
5323 {
5324   Feld[x][y] = EL_EMPTY;
5325
5326   MovPos[x][y] = 0;
5327   MovDir[x][y] = 0;
5328   MovDelay[x][y] = 0;
5329
5330 #if USE_NEW_CUSTOM_VALUE
5331   CustomValue[x][y] = 0;
5332 #endif
5333
5334   AmoebaNr[x][y] = 0;
5335   ChangeDelay[x][y] = 0;
5336   ChangePage[x][y] = -1;
5337   Pushed[x][y] = FALSE;
5338
5339 #if 0
5340   ExplodeField[x][y] = EX_TYPE_NONE;
5341 #endif
5342
5343   GfxElement[x][y] = EL_UNDEFINED;
5344   GfxAction[x][y] = ACTION_DEFAULT;
5345   GfxDir[x][y] = MV_NONE;
5346 #if 0
5347   /* !!! this would prevent the removed tile from being redrawn !!! */
5348   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5349 #endif
5350 }
5351
5352 void RemoveMovingField(int x, int y)
5353 {
5354   int oldx = x, oldy = y, newx = x, newy = y;
5355   int element = Feld[x][y];
5356   int next_element = EL_UNDEFINED;
5357
5358   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5359     return;
5360
5361   if (IS_MOVING(x, y))
5362   {
5363     Moving2Blocked(x, y, &newx, &newy);
5364
5365     if (Feld[newx][newy] != EL_BLOCKED)
5366     {
5367       /* element is moving, but target field is not free (blocked), but
5368          already occupied by something different (example: acid pool);
5369          in this case, only remove the moving field, but not the target */
5370
5371       RemoveField(oldx, oldy);
5372
5373       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5374
5375       TEST_DrawLevelField(oldx, oldy);
5376
5377       return;
5378     }
5379   }
5380   else if (element == EL_BLOCKED)
5381   {
5382     Blocked2Moving(x, y, &oldx, &oldy);
5383     if (!IS_MOVING(oldx, oldy))
5384       return;
5385   }
5386
5387   if (element == EL_BLOCKED &&
5388       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394     next_element = get_next_element(Feld[oldx][oldy]);
5395
5396   RemoveField(oldx, oldy);
5397   RemoveField(newx, newy);
5398
5399   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401   if (next_element != EL_UNDEFINED)
5402     Feld[oldx][oldy] = next_element;
5403
5404   TEST_DrawLevelField(oldx, oldy);
5405   TEST_DrawLevelField(newx, newy);
5406 }
5407
5408 void DrawDynamite(int x, int y)
5409 {
5410   int sx = SCREENX(x), sy = SCREENY(y);
5411   int graphic = el2img(Feld[x][y]);
5412   int frame;
5413
5414   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5415     return;
5416
5417   if (IS_WALKABLE_INSIDE(Back[x][y]))
5418     return;
5419
5420   if (Back[x][y])
5421     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422   else if (Store[x][y])
5423     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5424
5425   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5426
5427   if (Back[x][y] || Store[x][y])
5428     DrawGraphicThruMask(sx, sy, graphic, frame);
5429   else
5430     DrawGraphic(sx, sy, graphic, frame);
5431 }
5432
5433 void CheckDynamite(int x, int y)
5434 {
5435   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5436   {
5437     MovDelay[x][y]--;
5438
5439     if (MovDelay[x][y] != 0)
5440     {
5441       DrawDynamite(x, y);
5442       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5443
5444       return;
5445     }
5446   }
5447
5448   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5449
5450   Bang(x, y);
5451 }
5452
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5454 {
5455   boolean num_checked_players = 0;
5456   int i;
5457
5458   for (i = 0; i < MAX_PLAYERS; i++)
5459   {
5460     if (stored_player[i].active)
5461     {
5462       int sx = stored_player[i].jx;
5463       int sy = stored_player[i].jy;
5464
5465       if (num_checked_players == 0)
5466       {
5467         *sx1 = *sx2 = sx;
5468         *sy1 = *sy2 = sy;
5469       }
5470       else
5471       {
5472         *sx1 = MIN(*sx1, sx);
5473         *sy1 = MIN(*sy1, sy);
5474         *sx2 = MAX(*sx2, sx);
5475         *sy2 = MAX(*sy2, sy);
5476       }
5477
5478       num_checked_players++;
5479     }
5480   }
5481 }
5482
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5484 {
5485   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5486
5487   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5488
5489   return (sx2 - sx1 < SCR_FIELDX &&
5490           sy2 - sy1 < SCR_FIELDY);
5491 }
5492
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5494 {
5495   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5496
5497   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5498
5499   *sx = (sx1 + sx2) / 2;
5500   *sy = (sy1 + sy2) / 2;
5501 }
5502
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504                         boolean center_screen, boolean quick_relocation)
5505 {
5506   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507   boolean no_delay = (tape.warp_forward);
5508   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5510
5511   if (quick_relocation)
5512   {
5513     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5514     {
5515       if (!level.shifted_relocation || center_screen)
5516       {
5517         /* quick relocation (without scrolling), with centering of screen */
5518
5519         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5520                     x > SBX_Right + MIDPOSX ? SBX_Right :
5521                     x - MIDPOSX);
5522
5523         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5525                     y - MIDPOSY);
5526       }
5527       else
5528       {
5529         /* quick relocation (without scrolling), but do not center screen */
5530
5531         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5532                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5533                                old_x - MIDPOSX);
5534
5535         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5537                                old_y - MIDPOSY);
5538
5539         int offset_x = x + (scroll_x - center_scroll_x);
5540         int offset_y = y + (scroll_y - center_scroll_y);
5541
5542         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5543                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544                     offset_x - MIDPOSX);
5545
5546         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548                     offset_y - MIDPOSY);
5549       }
5550     }
5551     else
5552     {
5553 #if 1
5554       if (!level.shifted_relocation || center_screen)
5555       {
5556         /* quick relocation (without scrolling), with centering of screen */
5557
5558         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5559                     x > SBX_Right + MIDPOSX ? SBX_Right :
5560                     x - MIDPOSX);
5561
5562         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564                     y - MIDPOSY);
5565       }
5566       else
5567       {
5568         /* quick relocation (without scrolling), but do not center screen */
5569
5570         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5571                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5572                                old_x - MIDPOSX);
5573
5574         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5576                                old_y - MIDPOSY);
5577
5578         int offset_x = x + (scroll_x - center_scroll_x);
5579         int offset_y = y + (scroll_y - center_scroll_y);
5580
5581         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5582                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583                     offset_x - MIDPOSX);
5584
5585         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587                     offset_y - MIDPOSY);
5588       }
5589 #else
5590       /* quick relocation (without scrolling), inside visible screen area */
5591
5592       int offset = game.scroll_delay_value;
5593
5594       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5595           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5597
5598       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5599           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5601
5602       /* don't scroll over playfield boundaries */
5603       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5605
5606       /* don't scroll over playfield boundaries */
5607       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5609 #endif
5610     }
5611
5612     RedrawPlayfield(TRUE, 0,0,0,0);
5613   }
5614   else
5615   {
5616 #if 1
5617     int scroll_xx, scroll_yy;
5618
5619     if (!level.shifted_relocation || center_screen)
5620     {
5621       /* visible relocation (with scrolling), with centering of screen */
5622
5623       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5624                    x > SBX_Right + MIDPOSX ? SBX_Right :
5625                    x - MIDPOSX);
5626
5627       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5629                    y - MIDPOSY);
5630     }
5631     else
5632     {
5633       /* visible relocation (with scrolling), but do not center screen */
5634
5635       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5636                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5637                              old_x - MIDPOSX);
5638
5639       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5641                              old_y - MIDPOSY);
5642
5643       int offset_x = x + (scroll_x - center_scroll_x);
5644       int offset_y = y + (scroll_y - center_scroll_y);
5645
5646       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5647                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648                    offset_x - MIDPOSX);
5649
5650       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652                    offset_y - MIDPOSY);
5653     }
5654
5655 #else
5656
5657     /* visible relocation (with scrolling), with centering of screen */
5658
5659     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5660                      x > SBX_Right + MIDPOSX ? SBX_Right :
5661                      x - MIDPOSX);
5662
5663     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665                      y - MIDPOSY);
5666 #endif
5667
5668     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5669
5670     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5671     {
5672       int dx = 0, dy = 0;
5673       int fx = FX, fy = FY;
5674
5675       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5677
5678       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5679         break;
5680
5681       scroll_x -= dx;
5682       scroll_y -= dy;
5683
5684       fx += dx * TILEX / 2;
5685       fy += dy * TILEY / 2;
5686
5687       ScrollLevel(dx, dy);
5688       DrawAllPlayers();
5689
5690       /* scroll in two steps of half tile size to make things smoother */
5691       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5692       FlushDisplay();
5693       Delay(wait_delay_value);
5694
5695       /* scroll second step to align at full tile size */
5696       BackToFront();
5697       Delay(wait_delay_value);
5698     }
5699
5700     DrawAllPlayers();
5701     BackToFront();
5702     Delay(wait_delay_value);
5703   }
5704 }
5705
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5707 {
5708   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709   int player_nr = GET_PLAYER_NR(el_player);
5710   struct PlayerInfo *player = &stored_player[player_nr];
5711   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712   boolean no_delay = (tape.warp_forward);
5713   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715   int old_jx = player->jx;
5716   int old_jy = player->jy;
5717   int old_element = Feld[old_jx][old_jy];
5718   int element = Feld[jx][jy];
5719   boolean player_relocated = (old_jx != jx || old_jy != jy);
5720
5721   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5723   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5725   int leave_side_horiz = move_dir_horiz;
5726   int leave_side_vert  = move_dir_vert;
5727   int enter_side = enter_side_horiz | enter_side_vert;
5728   int leave_side = leave_side_horiz | leave_side_vert;
5729
5730   if (player->GameOver)         /* do not reanimate dead player */
5731     return;
5732
5733   if (!player_relocated)        /* no need to relocate the player */
5734     return;
5735
5736   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5737   {
5738     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5739     DrawLevelField(jx, jy);
5740   }
5741
5742   if (player->present)
5743   {
5744     while (player->MovPos)
5745     {
5746       ScrollPlayer(player, SCROLL_GO_ON);
5747       ScrollScreen(NULL, SCROLL_GO_ON);
5748
5749       AdvanceFrameAndPlayerCounters(player->index_nr);
5750
5751       DrawPlayer(player);
5752
5753       BackToFront();
5754       Delay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         /* needed here only to cleanup last field */
5758     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Feld[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5776   {
5777     Feld[jx][jy] = element;
5778     InitField(jx, jy, FALSE);
5779   }
5780
5781   /* only visually relocate centered player */
5782   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783                      FALSE, level.instant_relocation);
5784
5785   TestIfPlayerTouchesBadThing(jx, jy);
5786   TestIfPlayerTouchesCustomElement(jx, jy);
5787
5788   if (IS_CUSTOM_ELEMENT(element))
5789     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790                                player->index_bit, enter_side);
5791
5792   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793                                       player->index_bit, enter_side);
5794
5795 #if 1
5796   if (player->is_switching)
5797   {
5798     /* ensure that relocation while still switching an element does not cause
5799        a new element to be treated as also switched directly after relocation
5800        (this is important for teleporter switches that teleport the player to
5801        a place where another teleporter switch is in the same direction, which
5802        would then incorrectly be treated as immediately switched before the
5803        direction key that caused the switch was released) */
5804
5805     player->switch_x += jx - old_jx;
5806     player->switch_y += jy - old_jy;
5807   }
5808 #endif
5809 }
5810
5811 void Explode(int ex, int ey, int phase, int mode)
5812 {
5813   int x, y;
5814   int last_phase;
5815   int border_element;
5816
5817   /* !!! eliminate this variable !!! */
5818   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5819
5820   if (game.explosions_delayed)
5821   {
5822     ExplodeField[ex][ey] = mode;
5823     return;
5824   }
5825
5826   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5827   {
5828     int center_element = Feld[ex][ey];
5829     int artwork_element, explosion_element;     /* set these values later */
5830
5831 #if 0
5832     /* --- This is only really needed (and now handled) in "Impact()". --- */
5833     /* do not explode moving elements that left the explode field in time */
5834     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5835         center_element == EL_EMPTY &&
5836         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5837       return;
5838 #endif
5839
5840 #if 0
5841     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5842     if (mode == EX_TYPE_NORMAL ||
5843         mode == EX_TYPE_CENTER ||
5844         mode == EX_TYPE_CROSS)
5845       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5846 #endif
5847
5848     /* remove things displayed in background while burning dynamite */
5849     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5850       Back[ex][ey] = 0;
5851
5852     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5853     {
5854       /* put moving element to center field (and let it explode there) */
5855       center_element = MovingOrBlocked2Element(ex, ey);
5856       RemoveMovingField(ex, ey);
5857       Feld[ex][ey] = center_element;
5858     }
5859
5860     /* now "center_element" is finally determined -- set related values now */
5861     artwork_element = center_element;           /* for custom player artwork */
5862     explosion_element = center_element;         /* for custom player artwork */
5863
5864     if (IS_PLAYER(ex, ey))
5865     {
5866       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5867
5868       artwork_element = stored_player[player_nr].artwork_element;
5869
5870       if (level.use_explosion_element[player_nr])
5871       {
5872         explosion_element = level.explosion_element[player_nr];
5873         artwork_element = explosion_element;
5874       }
5875     }
5876
5877 #if 1
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882 #endif
5883
5884     last_phase = element_info[explosion_element].explosion_delay + 1;
5885
5886     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5887     {
5888       int xx = x - ex + 1;
5889       int yy = y - ey + 1;
5890       int element;
5891
5892       if (!IN_LEV_FIELD(x, y) ||
5893           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5895         continue;
5896
5897       element = Feld[x][y];
5898
5899       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5900       {
5901         element = MovingOrBlocked2Element(x, y);
5902
5903         if (!IS_EXPLOSION_PROOF(element))
5904           RemoveMovingField(x, y);
5905       }
5906
5907       /* indestructible elements can only explode in center (but not flames) */
5908       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909                                            mode == EX_TYPE_BORDER)) ||
5910           element == EL_FLAMES)
5911         continue;
5912
5913       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914          behaviour, for example when touching a yamyam that explodes to rocks
5915          with active deadly shield, a rock is created under the player !!! */
5916       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5917 #if 0
5918       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5921 #else
5922       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5923 #endif
5924       {
5925         if (IS_ACTIVE_BOMB(element))
5926         {
5927           /* re-activate things under the bomb like gate or penguin */
5928           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5929           Back[x][y] = 0;
5930         }
5931
5932         continue;
5933       }
5934
5935       /* save walkable background elements while explosion on same tile */
5936       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938         Back[x][y] = element;
5939
5940       /* ignite explodable elements reached by other explosion */
5941       if (element == EL_EXPLOSION)
5942         element = Store2[x][y];
5943
5944       if (AmoebaNr[x][y] &&
5945           (element == EL_AMOEBA_FULL ||
5946            element == EL_BD_AMOEBA ||
5947            element == EL_AMOEBA_GROWING))
5948       {
5949         AmoebaCnt[AmoebaNr[x][y]]--;
5950         AmoebaCnt2[AmoebaNr[x][y]]--;
5951       }
5952
5953       RemoveField(x, y);
5954
5955       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5956       {
5957         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5958
5959         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5960
5961         if (PLAYERINFO(ex, ey)->use_murphy)
5962           Store[x][y] = EL_EMPTY;
5963       }
5964
5965       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5966          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5967       else if (ELEM_IS_PLAYER(center_element))
5968         Store[x][y] = EL_EMPTY;
5969       else if (center_element == EL_YAMYAM)
5970         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972         Store[x][y] = element_info[center_element].content.e[xx][yy];
5973 #if 1
5974       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976          otherwise) -- FIX THIS !!! */
5977       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978         Store[x][y] = element_info[element].content.e[1][1];
5979 #else
5980       else if (!CAN_EXPLODE(element))
5981         Store[x][y] = element_info[element].content.e[1][1];
5982 #endif
5983       else
5984         Store[x][y] = EL_EMPTY;
5985
5986       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987           center_element == EL_AMOEBA_TO_DIAMOND)
5988         Store2[x][y] = element;
5989
5990       Feld[x][y] = EL_EXPLOSION;
5991       GfxElement[x][y] = artwork_element;
5992
5993       ExplodePhase[x][y] = 1;
5994       ExplodeDelay[x][y] = last_phase;
5995
5996       Stop[x][y] = TRUE;
5997     }
5998
5999     if (center_element == EL_YAMYAM)
6000       game.yamyam_content_nr =
6001         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6002
6003     return;
6004   }
6005
6006   if (Stop[ex][ey])
6007     return;
6008
6009   x = ex;
6010   y = ey;
6011
6012   if (phase == 1)
6013     GfxFrame[x][y] = 0;         /* restart explosion animation */
6014
6015   last_phase = ExplodeDelay[x][y];
6016
6017   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6018
6019 #ifdef DEBUG
6020
6021   /* activate this even in non-DEBUG version until cause for crash in
6022      getGraphicAnimationFrame() (see below) is found and eliminated */
6023
6024 #endif
6025 #if 1
6026
6027 #if 1
6028   /* this can happen if the player leaves an explosion just in time */
6029   if (GfxElement[x][y] == EL_UNDEFINED)
6030     GfxElement[x][y] = EL_EMPTY;
6031 #else
6032   if (GfxElement[x][y] == EL_UNDEFINED)
6033   {
6034     printf("\n\n");
6035     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6036     printf("Explode(): This should never happen!\n");
6037     printf("\n\n");
6038
6039     GfxElement[x][y] = EL_EMPTY;
6040   }
6041 #endif
6042
6043 #endif
6044
6045   border_element = Store2[x][y];
6046   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047     border_element = StorePlayer[x][y];
6048
6049   if (phase == element_info[border_element].ignition_delay ||
6050       phase == last_phase)
6051   {
6052     boolean border_explosion = FALSE;
6053
6054     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055         !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     {
6057       KillPlayerUnlessExplosionProtected(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6061     {
6062       Feld[x][y] = Store2[x][y];
6063       Store2[x][y] = 0;
6064       Bang(x, y);
6065       border_explosion = TRUE;
6066     }
6067     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6068     {
6069       AmoebeUmwandeln(x, y);
6070       Store2[x][y] = 0;
6071       border_explosion = TRUE;
6072     }
6073
6074     /* if an element just explodes due to another explosion (chain-reaction),
6075        do not immediately end the new explosion when it was the last frame of
6076        the explosion (as it would be done in the following "if"-statement!) */
6077     if (border_explosion && phase == last_phase)
6078       return;
6079   }
6080
6081   if (phase == last_phase)
6082   {
6083     int element;
6084
6085     element = Feld[x][y] = Store[x][y];
6086     Store[x][y] = Store2[x][y] = 0;
6087     GfxElement[x][y] = EL_UNDEFINED;
6088
6089     /* player can escape from explosions and might therefore be still alive */
6090     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091         element <= EL_PLAYER_IS_EXPLODING_4)
6092     {
6093       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094       int explosion_element = EL_PLAYER_1 + player_nr;
6095       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6097
6098       if (level.use_explosion_element[player_nr])
6099         explosion_element = level.explosion_element[player_nr];
6100
6101       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102                     element_info[explosion_element].content.e[xx][yy]);
6103     }
6104
6105     /* restore probably existing indestructible background element */
6106     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107       element = Feld[x][y] = Back[x][y];
6108     Back[x][y] = 0;
6109
6110     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111     GfxDir[x][y] = MV_NONE;
6112     ChangeDelay[x][y] = 0;
6113     ChangePage[x][y] = -1;
6114
6115 #if USE_NEW_CUSTOM_VALUE
6116     CustomValue[x][y] = 0;
6117 #endif
6118
6119     InitField_WithBug2(x, y, FALSE);
6120
6121     TEST_DrawLevelField(x, y);
6122
6123     TestIfElementTouchesCustomElement(x, y);
6124
6125     if (GFX_CRUMBLED(element))
6126       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6127
6128     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6129       StorePlayer[x][y] = 0;
6130
6131     if (ELEM_IS_PLAYER(element))
6132       RelocatePlayer(x, y, element);
6133   }
6134   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6135   {
6136     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6137     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6138
6139     if (phase == delay)
6140       TEST_DrawLevelFieldCrumbledSand(x, y);
6141
6142     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6143     {
6144       DrawLevelElement(x, y, Back[x][y]);
6145       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6146     }
6147     else if (IS_WALKABLE_UNDER(Back[x][y]))
6148     {
6149       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150       DrawLevelElementThruMask(x, y, Back[x][y]);
6151     }
6152     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6153       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6154   }
6155 }
6156
6157 void DynaExplode(int ex, int ey)
6158 {
6159   int i, j;
6160   int dynabomb_element = Feld[ex][ey];
6161   int dynabomb_size = 1;
6162   boolean dynabomb_xl = FALSE;
6163   struct PlayerInfo *player;
6164   static int xy[4][2] =
6165   {
6166     { 0, -1 },
6167     { -1, 0 },
6168     { +1, 0 },
6169     { 0, +1 }
6170   };
6171
6172   if (IS_ACTIVE_BOMB(dynabomb_element))
6173   {
6174     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6175     dynabomb_size = player->dynabomb_size;
6176     dynabomb_xl = player->dynabomb_xl;
6177     player->dynabombs_left++;
6178   }
6179
6180   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6181
6182   for (i = 0; i < NUM_DIRECTIONS; i++)
6183   {
6184     for (j = 1; j <= dynabomb_size; j++)
6185     {
6186       int x = ex + j * xy[i][0];
6187       int y = ey + j * xy[i][1];
6188       int element;
6189
6190       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6191         break;
6192
6193       element = Feld[x][y];
6194
6195       /* do not restart explosions of fields with active bombs */
6196       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6197         continue;
6198
6199       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6200
6201       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6202           !IS_DIGGABLE(element) && !dynabomb_xl)
6203         break;
6204     }
6205   }
6206 }
6207
6208 void Bang(int x, int y)
6209 {
6210   int element = MovingOrBlocked2Element(x, y);
6211   int explosion_type = EX_TYPE_NORMAL;
6212
6213   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6214   {
6215     struct PlayerInfo *player = PLAYERINFO(x, y);
6216
6217 #if USE_FIX_CE_ACTION_WITH_PLAYER
6218     element = Feld[x][y] = player->initial_element;
6219 #else
6220     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6221                             player->element_nr);
6222 #endif
6223
6224     if (level.use_explosion_element[player->index_nr])
6225     {
6226       int explosion_element = level.explosion_element[player->index_nr];
6227
6228       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6229         explosion_type = EX_TYPE_CROSS;
6230       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6231         explosion_type = EX_TYPE_CENTER;
6232     }
6233   }
6234
6235   switch (element)
6236   {
6237     case EL_BUG:
6238     case EL_SPACESHIP:
6239     case EL_BD_BUTTERFLY:
6240     case EL_BD_FIREFLY:
6241     case EL_YAMYAM:
6242     case EL_DARK_YAMYAM:
6243     case EL_ROBOT:
6244     case EL_PACMAN:
6245     case EL_MOLE:
6246       RaiseScoreElement(element);
6247       break;
6248
6249     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6250     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6251     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6252     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6253     case EL_DYNABOMB_INCREASE_NUMBER:
6254     case EL_DYNABOMB_INCREASE_SIZE:
6255     case EL_DYNABOMB_INCREASE_POWER:
6256       explosion_type = EX_TYPE_DYNA;
6257       break;
6258
6259     case EL_DC_LANDMINE:
6260 #if 0
6261     case EL_EM_EXIT_OPEN:
6262     case EL_EM_STEEL_EXIT_OPEN:
6263 #endif
6264       explosion_type = EX_TYPE_CENTER;
6265       break;
6266
6267     case EL_PENGUIN:
6268     case EL_LAMP:
6269     case EL_LAMP_ACTIVE:
6270     case EL_AMOEBA_TO_DIAMOND:
6271       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6272         explosion_type = EX_TYPE_CENTER;
6273       break;
6274
6275     default:
6276       if (element_info[element].explosion_type == EXPLODES_CROSS)
6277         explosion_type = EX_TYPE_CROSS;
6278       else if (element_info[element].explosion_type == EXPLODES_1X1)
6279         explosion_type = EX_TYPE_CENTER;
6280       break;
6281   }
6282
6283   if (explosion_type == EX_TYPE_DYNA)
6284     DynaExplode(x, y);
6285   else
6286     Explode(x, y, EX_PHASE_START, explosion_type);
6287
6288   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6289 }
6290
6291 void SplashAcid(int x, int y)
6292 {
6293   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6294       (!IN_LEV_FIELD(x - 1, y - 2) ||
6295        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6296     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6297
6298   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6299       (!IN_LEV_FIELD(x + 1, y - 2) ||
6300        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6301     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6302
6303   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6304 }
6305
6306 static void InitBeltMovement()
6307 {
6308   static int belt_base_element[4] =
6309   {
6310     EL_CONVEYOR_BELT_1_LEFT,
6311     EL_CONVEYOR_BELT_2_LEFT,
6312     EL_CONVEYOR_BELT_3_LEFT,
6313     EL_CONVEYOR_BELT_4_LEFT
6314   };
6315   static int belt_base_active_element[4] =
6316   {
6317     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6318     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6319     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6320     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6321   };
6322
6323   int x, y, i, j;
6324
6325   /* set frame order for belt animation graphic according to belt direction */
6326   for (i = 0; i < NUM_BELTS; i++)
6327   {
6328     int belt_nr = i;
6329
6330     for (j = 0; j < NUM_BELT_PARTS; j++)
6331     {
6332       int element = belt_base_active_element[belt_nr] + j;
6333       int graphic_1 = el2img(element);
6334       int graphic_2 = el2panelimg(element);
6335
6336       if (game.belt_dir[i] == MV_LEFT)
6337       {
6338         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6339         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6340       }
6341       else
6342       {
6343         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6344         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6345       }
6346     }
6347   }
6348
6349   SCAN_PLAYFIELD(x, y)
6350   {
6351     int element = Feld[x][y];
6352
6353     for (i = 0; i < NUM_BELTS; i++)
6354     {
6355       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6356       {
6357         int e_belt_nr = getBeltNrFromBeltElement(element);
6358         int belt_nr = i;
6359
6360         if (e_belt_nr == belt_nr)
6361         {
6362           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6363
6364           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6365         }
6366       }
6367     }
6368   }
6369 }
6370
6371 static void ToggleBeltSwitch(int x, int y)
6372 {
6373   static int belt_base_element[4] =
6374   {
6375     EL_CONVEYOR_BELT_1_LEFT,
6376     EL_CONVEYOR_BELT_2_LEFT,
6377     EL_CONVEYOR_BELT_3_LEFT,
6378     EL_CONVEYOR_BELT_4_LEFT
6379   };
6380   static int belt_base_active_element[4] =
6381   {
6382     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6383     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6384     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6385     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6386   };
6387   static int belt_base_switch_element[4] =
6388   {
6389     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6390     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6391     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6392     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6393   };
6394   static int belt_move_dir[4] =
6395   {
6396     MV_LEFT,
6397     MV_NONE,
6398     MV_RIGHT,
6399     MV_NONE,
6400   };
6401
6402   int element = Feld[x][y];
6403   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6404   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6405   int belt_dir = belt_move_dir[belt_dir_nr];
6406   int xx, yy, i;
6407
6408   if (!IS_BELT_SWITCH(element))
6409     return;
6410
6411   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6412   game.belt_dir[belt_nr] = belt_dir;
6413
6414   if (belt_dir_nr == 3)
6415     belt_dir_nr = 1;
6416
6417   /* set frame order for belt animation graphic according to belt direction */
6418   for (i = 0; i < NUM_BELT_PARTS; i++)
6419   {
6420     int element = belt_base_active_element[belt_nr] + i;
6421     int graphic_1 = el2img(element);
6422     int graphic_2 = el2panelimg(element);
6423
6424     if (belt_dir == MV_LEFT)
6425     {
6426       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6427       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6428     }
6429     else
6430     {
6431       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6432       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6433     }
6434   }
6435
6436   SCAN_PLAYFIELD(xx, yy)
6437   {
6438     int element = Feld[xx][yy];
6439
6440     if (IS_BELT_SWITCH(element))
6441     {
6442       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6443
6444       if (e_belt_nr == belt_nr)
6445       {
6446         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6447         TEST_DrawLevelField(xx, yy);
6448       }
6449     }
6450     else if (IS_BELT(element) && belt_dir != MV_NONE)
6451     {
6452       int e_belt_nr = getBeltNrFromBeltElement(element);
6453
6454       if (e_belt_nr == belt_nr)
6455       {
6456         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6457
6458         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6459         TEST_DrawLevelField(xx, yy);
6460       }
6461     }
6462     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6463     {
6464       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6465
6466       if (e_belt_nr == belt_nr)
6467       {
6468         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6469
6470         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6471         TEST_DrawLevelField(xx, yy);
6472       }
6473     }
6474   }
6475 }
6476
6477 static void ToggleSwitchgateSwitch(int x, int y)
6478 {
6479   int xx, yy;
6480
6481   game.switchgate_pos = !game.switchgate_pos;
6482
6483   SCAN_PLAYFIELD(xx, yy)
6484   {
6485     int element = Feld[xx][yy];
6486
6487 #if !USE_BOTH_SWITCHGATE_SWITCHES
6488     if (element == EL_SWITCHGATE_SWITCH_UP ||
6489         element == EL_SWITCHGATE_SWITCH_DOWN)
6490     {
6491       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6495              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6496     {
6497       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6498       TEST_DrawLevelField(xx, yy);
6499     }
6500 #else
6501     if (element == EL_SWITCHGATE_SWITCH_UP)
6502     {
6503       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6504       TEST_DrawLevelField(xx, yy);
6505     }
6506     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6507     {
6508       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6509       TEST_DrawLevelField(xx, yy);
6510     }
6511     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6512     {
6513       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6514       TEST_DrawLevelField(xx, yy);
6515     }
6516     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6517     {
6518       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6519       TEST_DrawLevelField(xx, yy);
6520     }
6521 #endif
6522     else if (element == EL_SWITCHGATE_OPEN ||
6523              element == EL_SWITCHGATE_OPENING)
6524     {
6525       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6526
6527       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6528     }
6529     else if (element == EL_SWITCHGATE_CLOSED ||
6530              element == EL_SWITCHGATE_CLOSING)
6531     {
6532       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6533
6534       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6535     }
6536   }
6537 }
6538
6539 static int getInvisibleActiveFromInvisibleElement(int element)
6540 {
6541   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6542           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6543           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6544           element);
6545 }
6546
6547 static int getInvisibleFromInvisibleActiveElement(int element)
6548 {
6549   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6550           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6551           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6552           element);
6553 }
6554
6555 static void RedrawAllLightSwitchesAndInvisibleElements()
6556 {
6557   int x, y;
6558
6559   SCAN_PLAYFIELD(x, y)
6560   {
6561     int element = Feld[x][y];
6562
6563     if (element == EL_LIGHT_SWITCH &&
6564         game.light_time_left > 0)
6565     {
6566       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6567       TEST_DrawLevelField(x, y);
6568     }
6569     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6570              game.light_time_left == 0)
6571     {
6572       Feld[x][y] = EL_LIGHT_SWITCH;
6573       TEST_DrawLevelField(x, y);
6574     }
6575     else if (element == EL_EMC_DRIPPER &&
6576              game.light_time_left > 0)
6577     {
6578       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582              game.light_time_left == 0)
6583     {
6584       Feld[x][y] = EL_EMC_DRIPPER;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL ||
6588              element == EL_INVISIBLE_WALL ||
6589              element == EL_INVISIBLE_SAND)
6590     {
6591       if (game.light_time_left > 0)
6592         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       /* uncrumble neighbour fields, if needed */
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6599     }
6600     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601              element == EL_INVISIBLE_WALL_ACTIVE ||
6602              element == EL_INVISIBLE_SAND_ACTIVE)
6603     {
6604       if (game.light_time_left == 0)
6605         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6606
6607       TEST_DrawLevelField(x, y);
6608
6609       /* re-crumble neighbour fields, if needed */
6610       if (element == EL_INVISIBLE_SAND)
6611         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6612     }
6613   }
6614 }
6615
6616 static void RedrawAllInvisibleElementsForLenses()
6617 {
6618   int x, y;
6619
6620   SCAN_PLAYFIELD(x, y)
6621   {
6622     int element = Feld[x][y];
6623
6624     if (element == EL_EMC_DRIPPER &&
6625         game.lenses_time_left > 0)
6626     {
6627       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631              game.lenses_time_left == 0)
6632     {
6633       Feld[x][y] = EL_EMC_DRIPPER;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_INVISIBLE_STEELWALL ||
6637              element == EL_INVISIBLE_WALL ||
6638              element == EL_INVISIBLE_SAND)
6639     {
6640       if (game.lenses_time_left > 0)
6641         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6642
6643       TEST_DrawLevelField(x, y);
6644
6645       /* uncrumble neighbour fields, if needed */
6646       if (element == EL_INVISIBLE_SAND)
6647         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6648     }
6649     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650              element == EL_INVISIBLE_WALL_ACTIVE ||
6651              element == EL_INVISIBLE_SAND_ACTIVE)
6652     {
6653       if (game.lenses_time_left == 0)
6654         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6655
6656       TEST_DrawLevelField(x, y);
6657
6658       /* re-crumble neighbour fields, if needed */
6659       if (element == EL_INVISIBLE_SAND)
6660         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6661     }
6662   }
6663 }
6664
6665 static void RedrawAllInvisibleElementsForMagnifier()
6666 {
6667   int x, y;
6668
6669   SCAN_PLAYFIELD(x, y)
6670   {
6671     int element = Feld[x][y];
6672
6673     if (element == EL_EMC_FAKE_GRASS &&
6674         game.magnify_time_left > 0)
6675     {
6676       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6677       TEST_DrawLevelField(x, y);
6678     }
6679     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6680              game.magnify_time_left == 0)
6681     {
6682       Feld[x][y] = EL_EMC_FAKE_GRASS;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (IS_GATE_GRAY(element) &&
6686              game.magnify_time_left > 0)
6687     {
6688       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6689                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6690                     IS_EM_GATE_GRAY(element) ?
6691                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6692                     IS_EMC_GATE_GRAY(element) ?
6693                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6694                     IS_DC_GATE_GRAY(element) ?
6695                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6696                     element);
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (IS_GATE_GRAY_ACTIVE(element) &&
6700              game.magnify_time_left == 0)
6701     {
6702       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6703                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6704                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6705                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6706                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6707                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6708                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6709                     EL_DC_GATE_WHITE_GRAY :
6710                     element);
6711       TEST_DrawLevelField(x, y);
6712     }
6713   }
6714 }
6715
6716 static void ToggleLightSwitch(int x, int y)
6717 {
6718   int element = Feld[x][y];
6719
6720   game.light_time_left =
6721     (element == EL_LIGHT_SWITCH ?
6722      level.time_light * FRAMES_PER_SECOND : 0);
6723
6724   RedrawAllLightSwitchesAndInvisibleElements();
6725 }
6726
6727 static void ActivateTimegateSwitch(int x, int y)
6728 {
6729   int xx, yy;
6730
6731   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6732
6733   SCAN_PLAYFIELD(xx, yy)
6734   {
6735     int element = Feld[xx][yy];
6736
6737     if (element == EL_TIMEGATE_CLOSED ||
6738         element == EL_TIMEGATE_CLOSING)
6739     {
6740       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6741       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6742     }
6743
6744     /*
6745     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6746     {
6747       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6748       TEST_DrawLevelField(xx, yy);
6749     }
6750     */
6751
6752   }
6753
6754 #if 1
6755   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6756                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6757 #else
6758   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6759 #endif
6760 }
6761
6762 void Impact(int x, int y)
6763 {
6764   boolean last_line = (y == lev_fieldy - 1);
6765   boolean object_hit = FALSE;
6766   boolean impact = (last_line || object_hit);
6767   int element = Feld[x][y];
6768   int smashed = EL_STEELWALL;
6769
6770   if (!last_line)       /* check if element below was hit */
6771   {
6772     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6773       return;
6774
6775     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6776                                          MovDir[x][y + 1] != MV_DOWN ||
6777                                          MovPos[x][y + 1] <= TILEY / 2));
6778
6779     /* do not smash moving elements that left the smashed field in time */
6780     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6781         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6782       object_hit = FALSE;
6783
6784 #if USE_QUICKSAND_IMPACT_BUGFIX
6785     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6786     {
6787       RemoveMovingField(x, y + 1);
6788       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6789       Feld[x][y + 2] = EL_ROCK;
6790       TEST_DrawLevelField(x, y + 2);
6791
6792       object_hit = TRUE;
6793     }
6794
6795     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6796     {
6797       RemoveMovingField(x, y + 1);
6798       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6799       Feld[x][y + 2] = EL_ROCK;
6800       TEST_DrawLevelField(x, y + 2);
6801
6802       object_hit = TRUE;
6803     }
6804 #endif
6805
6806     if (object_hit)
6807       smashed = MovingOrBlocked2Element(x, y + 1);
6808
6809     impact = (last_line || object_hit);
6810   }
6811
6812   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6813   {
6814     SplashAcid(x, y + 1);
6815     return;
6816   }
6817
6818   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6819   /* only reset graphic animation if graphic really changes after impact */
6820   if (impact &&
6821       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6822   {
6823     ResetGfxAnimation(x, y);
6824     TEST_DrawLevelField(x, y);
6825   }
6826
6827   if (impact && CAN_EXPLODE_IMPACT(element))
6828   {
6829     Bang(x, y);
6830     return;
6831   }
6832   else if (impact && element == EL_PEARL &&
6833            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6834   {
6835     ResetGfxAnimation(x, y);
6836
6837     Feld[x][y] = EL_PEARL_BREAKING;
6838     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6839     return;
6840   }
6841   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6842   {
6843     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6844
6845     return;
6846   }
6847
6848   if (impact && element == EL_AMOEBA_DROP)
6849   {
6850     if (object_hit && IS_PLAYER(x, y + 1))
6851       KillPlayerUnlessEnemyProtected(x, y + 1);
6852     else if (object_hit && smashed == EL_PENGUIN)
6853       Bang(x, y + 1);
6854     else
6855     {
6856       Feld[x][y] = EL_AMOEBA_GROWING;
6857       Store[x][y] = EL_AMOEBA_WET;
6858
6859       ResetRandomAnimationValue(x, y);
6860     }
6861     return;
6862   }
6863
6864   if (object_hit)               /* check which object was hit */
6865   {
6866     if ((CAN_PASS_MAGIC_WALL(element) && 
6867          (smashed == EL_MAGIC_WALL ||
6868           smashed == EL_BD_MAGIC_WALL)) ||
6869         (CAN_PASS_DC_MAGIC_WALL(element) &&
6870          smashed == EL_DC_MAGIC_WALL))
6871     {
6872       int xx, yy;
6873       int activated_magic_wall =
6874         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6875          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6876          EL_DC_MAGIC_WALL_ACTIVE);
6877
6878       /* activate magic wall / mill */
6879       SCAN_PLAYFIELD(xx, yy)
6880       {
6881         if (Feld[xx][yy] == smashed)
6882           Feld[xx][yy] = activated_magic_wall;
6883       }
6884
6885       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6886       game.magic_wall_active = TRUE;
6887
6888       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6889                             SND_MAGIC_WALL_ACTIVATING :
6890                             smashed == EL_BD_MAGIC_WALL ?
6891                             SND_BD_MAGIC_WALL_ACTIVATING :
6892                             SND_DC_MAGIC_WALL_ACTIVATING));
6893     }
6894
6895     if (IS_PLAYER(x, y + 1))
6896     {
6897       if (CAN_SMASH_PLAYER(element))
6898       {
6899         KillPlayerUnlessEnemyProtected(x, y + 1);
6900         return;
6901       }
6902     }
6903     else if (smashed == EL_PENGUIN)
6904     {
6905       if (CAN_SMASH_PLAYER(element))
6906       {
6907         Bang(x, y + 1);
6908         return;
6909       }
6910     }
6911     else if (element == EL_BD_DIAMOND)
6912     {
6913       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6914       {
6915         Bang(x, y + 1);
6916         return;
6917       }
6918     }
6919     else if (((element == EL_SP_INFOTRON ||
6920                element == EL_SP_ZONK) &&
6921               (smashed == EL_SP_SNIKSNAK ||
6922                smashed == EL_SP_ELECTRON ||
6923                smashed == EL_SP_DISK_ORANGE)) ||
6924              (element == EL_SP_INFOTRON &&
6925               smashed == EL_SP_DISK_YELLOW))
6926     {
6927       Bang(x, y + 1);
6928       return;
6929     }
6930     else if (CAN_SMASH_EVERYTHING(element))
6931     {
6932       if (IS_CLASSIC_ENEMY(smashed) ||
6933           CAN_EXPLODE_SMASHED(smashed))
6934       {
6935         Bang(x, y + 1);
6936         return;
6937       }
6938       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6939       {
6940         if (smashed == EL_LAMP ||
6941             smashed == EL_LAMP_ACTIVE)
6942         {
6943           Bang(x, y + 1);
6944           return;
6945         }
6946         else if (smashed == EL_NUT)
6947         {
6948           Feld[x][y + 1] = EL_NUT_BREAKING;
6949           PlayLevelSound(x, y, SND_NUT_BREAKING);
6950           RaiseScoreElement(EL_NUT);
6951           return;
6952         }
6953         else if (smashed == EL_PEARL)
6954         {
6955           ResetGfxAnimation(x, y);
6956
6957           Feld[x][y + 1] = EL_PEARL_BREAKING;
6958           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6959           return;
6960         }
6961         else if (smashed == EL_DIAMOND)
6962         {
6963           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6964           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6965           return;
6966         }
6967         else if (IS_BELT_SWITCH(smashed))
6968         {
6969           ToggleBeltSwitch(x, y + 1);
6970         }
6971         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6972                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6973                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6974                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6975         {
6976           ToggleSwitchgateSwitch(x, y + 1);
6977         }
6978         else if (smashed == EL_LIGHT_SWITCH ||
6979                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6980         {
6981           ToggleLightSwitch(x, y + 1);
6982         }
6983         else
6984         {
6985 #if 0
6986           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6987 #endif
6988
6989           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6990
6991           CheckElementChangeBySide(x, y + 1, smashed, element,
6992                                    CE_SWITCHED, CH_SIDE_TOP);
6993           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6994                                             CH_SIDE_TOP);
6995         }
6996       }
6997       else
6998       {
6999         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7000       }
7001     }
7002   }
7003
7004   /* play sound of magic wall / mill */
7005   if (!last_line &&
7006       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7007        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7008        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7009   {
7010     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7011       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7012     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7013       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7014     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7015       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7016
7017     return;
7018   }
7019
7020   /* play sound of object that hits the ground */
7021   if (last_line || object_hit)
7022     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7023 }
7024
7025 inline static void TurnRoundExt(int x, int y)
7026 {
7027   static struct
7028   {
7029     int dx, dy;
7030   } move_xy[] =
7031   {
7032     {  0,  0 },
7033     { -1,  0 },
7034     { +1,  0 },
7035     {  0,  0 },
7036     {  0, -1 },
7037     {  0,  0 }, { 0, 0 }, { 0, 0 },
7038     {  0, +1 }
7039   };
7040   static struct
7041   {
7042     int left, right, back;
7043   } turn[] =
7044   {
7045     { 0,        0,              0        },
7046     { MV_DOWN,  MV_UP,          MV_RIGHT },
7047     { MV_UP,    MV_DOWN,        MV_LEFT  },
7048     { 0,        0,              0        },
7049     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7050     { 0,        0,              0        },
7051     { 0,        0,              0        },
7052     { 0,        0,              0        },
7053     { MV_RIGHT, MV_LEFT,        MV_UP    }
7054   };
7055
7056   int element = Feld[x][y];
7057   int move_pattern = element_info[element].move_pattern;
7058
7059   int old_move_dir = MovDir[x][y];
7060   int left_dir  = turn[old_move_dir].left;
7061   int right_dir = turn[old_move_dir].right;
7062   int back_dir  = turn[old_move_dir].back;
7063
7064   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7065   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7066   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7067   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7068
7069   int left_x  = x + left_dx,  left_y  = y + left_dy;
7070   int right_x = x + right_dx, right_y = y + right_dy;
7071   int move_x  = x + move_dx,  move_y  = y + move_dy;
7072
7073   int xx, yy;
7074
7075   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7076   {
7077     TestIfBadThingTouchesOtherBadThing(x, y);
7078
7079     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7080       MovDir[x][y] = right_dir;
7081     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7082       MovDir[x][y] = left_dir;
7083
7084     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7085       MovDelay[x][y] = 9;
7086     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7087       MovDelay[x][y] = 1;
7088   }
7089   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7090   {
7091     TestIfBadThingTouchesOtherBadThing(x, y);
7092
7093     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7094       MovDir[x][y] = left_dir;
7095     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7096       MovDir[x][y] = right_dir;
7097
7098     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7099       MovDelay[x][y] = 9;
7100     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7101       MovDelay[x][y] = 1;
7102   }
7103   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7104   {
7105     TestIfBadThingTouchesOtherBadThing(x, y);
7106
7107     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7108       MovDir[x][y] = left_dir;
7109     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7110       MovDir[x][y] = right_dir;
7111
7112     if (MovDir[x][y] != old_move_dir)
7113       MovDelay[x][y] = 9;
7114   }
7115   else if (element == EL_YAMYAM)
7116   {
7117     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7118     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7119
7120     if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7122     else if (can_turn_left)
7123       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7124     else if (can_turn_right)
7125       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     MovDelay[x][y] = 16 + 16 * RND(3);
7130   }
7131   else if (element == EL_DARK_YAMYAM)
7132   {
7133     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7134                                                          left_x, left_y);
7135     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7136                                                          right_x, right_y);
7137
7138     if (can_turn_left && can_turn_right)
7139       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7140     else if (can_turn_left)
7141       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7142     else if (can_turn_right)
7143       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7144     else
7145       MovDir[x][y] = back_dir;
7146
7147     MovDelay[x][y] = 16 + 16 * RND(3);
7148   }
7149   else if (element == EL_PACMAN)
7150   {
7151     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7152     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7153
7154     if (can_turn_left && can_turn_right)
7155       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7156     else if (can_turn_left)
7157       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7158     else if (can_turn_right)
7159       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7160     else
7161       MovDir[x][y] = back_dir;
7162
7163     MovDelay[x][y] = 6 + RND(40);
7164   }
7165   else if (element == EL_PIG)
7166   {
7167     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7168     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7169     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7170     boolean should_turn_left, should_turn_right, should_move_on;
7171     int rnd_value = 24;
7172     int rnd = RND(rnd_value);
7173
7174     should_turn_left = (can_turn_left &&
7175                         (!can_move_on ||
7176                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7177                                                    y + back_dy + left_dy)));
7178     should_turn_right = (can_turn_right &&
7179                          (!can_move_on ||
7180                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7181                                                     y + back_dy + right_dy)));
7182     should_move_on = (can_move_on &&
7183                       (!can_turn_left ||
7184                        !can_turn_right ||
7185                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7186                                                  y + move_dy + left_dy) ||
7187                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7188                                                  y + move_dy + right_dy)));
7189
7190     if (should_turn_left || should_turn_right || should_move_on)
7191     {
7192       if (should_turn_left && should_turn_right && should_move_on)
7193         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7194                         rnd < 2 * rnd_value / 3 ? right_dir :
7195                         old_move_dir);
7196       else if (should_turn_left && should_turn_right)
7197         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7198       else if (should_turn_left && should_move_on)
7199         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7200       else if (should_turn_right && should_move_on)
7201         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7202       else if (should_turn_left)
7203         MovDir[x][y] = left_dir;
7204       else if (should_turn_right)
7205         MovDir[x][y] = right_dir;
7206       else if (should_move_on)
7207         MovDir[x][y] = old_move_dir;
7208     }
7209     else if (can_move_on && rnd > rnd_value / 8)
7210       MovDir[x][y] = old_move_dir;
7211     else if (can_turn_left && can_turn_right)
7212       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213     else if (can_turn_left && rnd > rnd_value / 8)
7214       MovDir[x][y] = left_dir;
7215     else if (can_turn_right && rnd > rnd_value/8)
7216       MovDir[x][y] = right_dir;
7217     else
7218       MovDir[x][y] = back_dir;
7219
7220     xx = x + move_xy[MovDir[x][y]].dx;
7221     yy = y + move_xy[MovDir[x][y]].dy;
7222
7223     if (!IN_LEV_FIELD(xx, yy) ||
7224         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7225       MovDir[x][y] = old_move_dir;
7226
7227     MovDelay[x][y] = 0;
7228   }
7229   else if (element == EL_DRAGON)
7230   {
7231     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7232     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7233     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7234     int rnd_value = 24;
7235     int rnd = RND(rnd_value);
7236
7237     if (can_move_on && rnd > rnd_value / 8)
7238       MovDir[x][y] = old_move_dir;
7239     else if (can_turn_left && can_turn_right)
7240       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7241     else if (can_turn_left && rnd > rnd_value / 8)
7242       MovDir[x][y] = left_dir;
7243     else if (can_turn_right && rnd > rnd_value / 8)
7244       MovDir[x][y] = right_dir;
7245     else
7246       MovDir[x][y] = back_dir;
7247
7248     xx = x + move_xy[MovDir[x][y]].dx;
7249     yy = y + move_xy[MovDir[x][y]].dy;
7250
7251     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7252       MovDir[x][y] = old_move_dir;
7253
7254     MovDelay[x][y] = 0;
7255   }
7256   else if (element == EL_MOLE)
7257   {
7258     boolean can_move_on =
7259       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7260                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7261                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7262     if (!can_move_on)
7263     {
7264       boolean can_turn_left =
7265         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7266                               IS_AMOEBOID(Feld[left_x][left_y])));
7267
7268       boolean can_turn_right =
7269         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7270                               IS_AMOEBOID(Feld[right_x][right_y])));
7271
7272       if (can_turn_left && can_turn_right)
7273         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7274       else if (can_turn_left)
7275         MovDir[x][y] = left_dir;
7276       else
7277         MovDir[x][y] = right_dir;
7278     }
7279
7280     if (MovDir[x][y] != old_move_dir)
7281       MovDelay[x][y] = 9;
7282   }
7283   else if (element == EL_BALLOON)
7284   {
7285     MovDir[x][y] = game.wind_direction;
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (element == EL_SPRING)
7289   {
7290 #if USE_NEW_SPRING_BUMPER
7291     if (MovDir[x][y] & MV_HORIZONTAL)
7292     {
7293       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7294           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7295       {
7296         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7297         ResetGfxAnimation(move_x, move_y);
7298         TEST_DrawLevelField(move_x, move_y);
7299
7300         MovDir[x][y] = back_dir;
7301       }
7302       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7303                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304         MovDir[x][y] = MV_NONE;
7305     }
7306 #else
7307     if (MovDir[x][y] & MV_HORIZONTAL &&
7308         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7309          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7310       MovDir[x][y] = MV_NONE;
7311 #endif
7312
7313     MovDelay[x][y] = 0;
7314   }
7315   else if (element == EL_ROBOT ||
7316            element == EL_SATELLITE ||
7317            element == EL_PENGUIN ||
7318            element == EL_EMC_ANDROID)
7319   {
7320     int attr_x = -1, attr_y = -1;
7321
7322     if (AllPlayersGone)
7323     {
7324       attr_x = ExitX;
7325       attr_y = ExitY;
7326     }
7327     else
7328     {
7329       int i;
7330
7331       for (i = 0; i < MAX_PLAYERS; i++)
7332       {
7333         struct PlayerInfo *player = &stored_player[i];
7334         int jx = player->jx, jy = player->jy;
7335
7336         if (!player->active)
7337           continue;
7338
7339         if (attr_x == -1 ||
7340             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7341         {
7342           attr_x = jx;
7343           attr_y = jy;
7344         }
7345       }
7346     }
7347
7348     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7349         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7350          game.engine_version < VERSION_IDENT(3,1,0,0)))
7351     {
7352       attr_x = ZX;
7353       attr_y = ZY;
7354     }
7355
7356     if (element == EL_PENGUIN)
7357     {
7358       int i;
7359       static int xy[4][2] =
7360       {
7361         { 0, -1 },
7362         { -1, 0 },
7363         { +1, 0 },
7364         { 0, +1 }
7365       };
7366
7367       for (i = 0; i < NUM_DIRECTIONS; i++)
7368       {
7369         int ex = x + xy[i][0];
7370         int ey = y + xy[i][1];
7371
7372         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7373                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7374                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7375                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7376         {
7377           attr_x = ex;
7378           attr_y = ey;
7379           break;
7380         }
7381       }
7382     }
7383
7384     MovDir[x][y] = MV_NONE;
7385     if (attr_x < x)
7386       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7387     else if (attr_x > x)
7388       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7389     if (attr_y < y)
7390       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7391     else if (attr_y > y)
7392       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7393
7394     if (element == EL_ROBOT)
7395     {
7396       int newx, newy;
7397
7398       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7399         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7400       Moving2Blocked(x, y, &newx, &newy);
7401
7402       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7403         MovDelay[x][y] = 8 + 8 * !RND(3);
7404       else
7405         MovDelay[x][y] = 16;
7406     }
7407     else if (element == EL_PENGUIN)
7408     {
7409       int newx, newy;
7410
7411       MovDelay[x][y] = 1;
7412
7413       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7414       {
7415         boolean first_horiz = RND(2);
7416         int new_move_dir = MovDir[x][y];
7417
7418         MovDir[x][y] =
7419           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420         Moving2Blocked(x, y, &newx, &newy);
7421
7422         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7423           return;
7424
7425         MovDir[x][y] =
7426           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7427         Moving2Blocked(x, y, &newx, &newy);
7428
7429         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7430           return;
7431
7432         MovDir[x][y] = old_move_dir;
7433         return;
7434       }
7435     }
7436     else if (element == EL_SATELLITE)
7437     {
7438       int newx, newy;
7439
7440       MovDelay[x][y] = 1;
7441
7442       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443       {
7444         boolean first_horiz = RND(2);
7445         int new_move_dir = MovDir[x][y];
7446
7447         MovDir[x][y] =
7448           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449         Moving2Blocked(x, y, &newx, &newy);
7450
7451         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7452           return;
7453
7454         MovDir[x][y] =
7455           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456         Moving2Blocked(x, y, &newx, &newy);
7457
7458         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7459           return;
7460
7461         MovDir[x][y] = old_move_dir;
7462         return;
7463       }
7464     }
7465     else if (element == EL_EMC_ANDROID)
7466     {
7467       static int check_pos[16] =
7468       {
7469         -1,             /*  0 => (invalid)          */
7470         7,              /*  1 => MV_LEFT            */
7471         3,              /*  2 => MV_RIGHT           */
7472         -1,             /*  3 => (invalid)          */
7473         1,              /*  4 =>            MV_UP   */
7474         0,              /*  5 => MV_LEFT  | MV_UP   */
7475         2,              /*  6 => MV_RIGHT | MV_UP   */
7476         -1,             /*  7 => (invalid)          */
7477         5,              /*  8 =>            MV_DOWN */
7478         6,              /*  9 => MV_LEFT  | MV_DOWN */
7479         4,              /* 10 => MV_RIGHT | MV_DOWN */
7480         -1,             /* 11 => (invalid)          */
7481         -1,             /* 12 => (invalid)          */
7482         -1,             /* 13 => (invalid)          */
7483         -1,             /* 14 => (invalid)          */
7484         -1,             /* 15 => (invalid)          */
7485       };
7486       static struct
7487       {
7488         int dx, dy;
7489         int dir;
7490       } check_xy[8] =
7491       {
7492         { -1, -1,       MV_LEFT  | MV_UP   },
7493         {  0, -1,                  MV_UP   },
7494         { +1, -1,       MV_RIGHT | MV_UP   },
7495         { +1,  0,       MV_RIGHT           },
7496         { +1, +1,       MV_RIGHT | MV_DOWN },
7497         {  0, +1,                  MV_DOWN },
7498         { -1, +1,       MV_LEFT  | MV_DOWN },
7499         { -1,  0,       MV_LEFT            },
7500       };
7501       int start_pos, check_order;
7502       boolean can_clone = FALSE;
7503       int i;
7504
7505       /* check if there is any free field around current position */
7506       for (i = 0; i < 8; i++)
7507       {
7508         int newx = x + check_xy[i].dx;
7509         int newy = y + check_xy[i].dy;
7510
7511         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512         {
7513           can_clone = TRUE;
7514
7515           break;
7516         }
7517       }
7518
7519       if (can_clone)            /* randomly find an element to clone */
7520       {
7521         can_clone = FALSE;
7522
7523         start_pos = check_pos[RND(8)];
7524         check_order = (RND(2) ? -1 : +1);
7525
7526         for (i = 0; i < 8; i++)
7527         {
7528           int pos_raw = start_pos + i * check_order;
7529           int pos = (pos_raw + 8) % 8;
7530           int newx = x + check_xy[pos].dx;
7531           int newy = y + check_xy[pos].dy;
7532
7533           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7534           {
7535             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7536             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7537
7538             Store[x][y] = Feld[newx][newy];
7539
7540             can_clone = TRUE;
7541
7542             break;
7543           }
7544         }
7545       }
7546
7547       if (can_clone)            /* randomly find a direction to move */
7548       {
7549         can_clone = FALSE;
7550
7551         start_pos = check_pos[RND(8)];
7552         check_order = (RND(2) ? -1 : +1);
7553
7554         for (i = 0; i < 8; i++)
7555         {
7556           int pos_raw = start_pos + i * check_order;
7557           int pos = (pos_raw + 8) % 8;
7558           int newx = x + check_xy[pos].dx;
7559           int newy = y + check_xy[pos].dy;
7560           int new_move_dir = check_xy[pos].dir;
7561
7562           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7563           {
7564             MovDir[x][y] = new_move_dir;
7565             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7566
7567             can_clone = TRUE;
7568
7569             break;
7570           }
7571         }
7572       }
7573
7574       if (can_clone)            /* cloning and moving successful */
7575         return;
7576
7577       /* cannot clone -- try to move towards player */
7578
7579       start_pos = check_pos[MovDir[x][y] & 0x0f];
7580       check_order = (RND(2) ? -1 : +1);
7581
7582       for (i = 0; i < 3; i++)
7583       {
7584         /* first check start_pos, then previous/next or (next/previous) pos */
7585         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7586         int pos = (pos_raw + 8) % 8;
7587         int newx = x + check_xy[pos].dx;
7588         int newy = y + check_xy[pos].dy;
7589         int new_move_dir = check_xy[pos].dir;
7590
7591         if (IS_PLAYER(newx, newy))
7592           break;
7593
7594         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7595         {
7596           MovDir[x][y] = new_move_dir;
7597           MovDelay[x][y] = level.android_move_time * 8 + 1;
7598
7599           break;
7600         }
7601       }
7602     }
7603   }
7604   else if (move_pattern == MV_TURNING_LEFT ||
7605            move_pattern == MV_TURNING_RIGHT ||
7606            move_pattern == MV_TURNING_LEFT_RIGHT ||
7607            move_pattern == MV_TURNING_RIGHT_LEFT ||
7608            move_pattern == MV_TURNING_RANDOM ||
7609            move_pattern == MV_ALL_DIRECTIONS)
7610   {
7611     boolean can_turn_left =
7612       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7613     boolean can_turn_right =
7614       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7615
7616     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7617       return;
7618
7619     if (move_pattern == MV_TURNING_LEFT)
7620       MovDir[x][y] = left_dir;
7621     else if (move_pattern == MV_TURNING_RIGHT)
7622       MovDir[x][y] = right_dir;
7623     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7624       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7625     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7626       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7627     else if (move_pattern == MV_TURNING_RANDOM)
7628       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7629                       can_turn_right && !can_turn_left ? right_dir :
7630                       RND(2) ? left_dir : right_dir);
7631     else if (can_turn_left && can_turn_right)
7632       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7633     else if (can_turn_left)
7634       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7635     else if (can_turn_right)
7636       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7637     else
7638       MovDir[x][y] = back_dir;
7639
7640     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641   }
7642   else if (move_pattern == MV_HORIZONTAL ||
7643            move_pattern == MV_VERTICAL)
7644   {
7645     if (move_pattern & old_move_dir)
7646       MovDir[x][y] = back_dir;
7647     else if (move_pattern == MV_HORIZONTAL)
7648       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7649     else if (move_pattern == MV_VERTICAL)
7650       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7651
7652     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7653   }
7654   else if (move_pattern & MV_ANY_DIRECTION)
7655   {
7656     MovDir[x][y] = move_pattern;
7657     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7658   }
7659   else if (move_pattern & MV_WIND_DIRECTION)
7660   {
7661     MovDir[x][y] = game.wind_direction;
7662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7663   }
7664   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7665   {
7666     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7667       MovDir[x][y] = left_dir;
7668     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7669       MovDir[x][y] = right_dir;
7670
7671     if (MovDir[x][y] != old_move_dir)
7672       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7673   }
7674   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7675   {
7676     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7677       MovDir[x][y] = right_dir;
7678     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7679       MovDir[x][y] = left_dir;
7680
7681     if (MovDir[x][y] != old_move_dir)
7682       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683   }
7684   else if (move_pattern == MV_TOWARDS_PLAYER ||
7685            move_pattern == MV_AWAY_FROM_PLAYER)
7686   {
7687     int attr_x = -1, attr_y = -1;
7688     int newx, newy;
7689     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7690
7691     if (AllPlayersGone)
7692     {
7693       attr_x = ExitX;
7694       attr_y = ExitY;
7695     }
7696     else
7697     {
7698       int i;
7699
7700       for (i = 0; i < MAX_PLAYERS; i++)
7701       {
7702         struct PlayerInfo *player = &stored_player[i];
7703         int jx = player->jx, jy = player->jy;
7704
7705         if (!player->active)
7706           continue;
7707
7708         if (attr_x == -1 ||
7709             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7710         {
7711           attr_x = jx;
7712           attr_y = jy;
7713         }
7714       }
7715     }
7716
7717     MovDir[x][y] = MV_NONE;
7718     if (attr_x < x)
7719       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7720     else if (attr_x > x)
7721       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7722     if (attr_y < y)
7723       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7724     else if (attr_y > y)
7725       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7726
7727     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7728
7729     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7730     {
7731       boolean first_horiz = RND(2);
7732       int new_move_dir = MovDir[x][y];
7733
7734       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7735       {
7736         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7737         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7738
7739         return;
7740       }
7741
7742       MovDir[x][y] =
7743         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744       Moving2Blocked(x, y, &newx, &newy);
7745
7746       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7747         return;
7748
7749       MovDir[x][y] =
7750         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7751       Moving2Blocked(x, y, &newx, &newy);
7752
7753       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7754         return;
7755
7756       MovDir[x][y] = old_move_dir;
7757     }
7758   }
7759   else if (move_pattern == MV_WHEN_PUSHED ||
7760            move_pattern == MV_WHEN_DROPPED)
7761   {
7762     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763       MovDir[x][y] = MV_NONE;
7764
7765     MovDelay[x][y] = 0;
7766   }
7767   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7768   {
7769     static int test_xy[7][2] =
7770     {
7771       { 0, -1 },
7772       { -1, 0 },
7773       { +1, 0 },
7774       { 0, +1 },
7775       { 0, -1 },
7776       { -1, 0 },
7777       { +1, 0 },
7778     };
7779     static int test_dir[7] =
7780     {
7781       MV_UP,
7782       MV_LEFT,
7783       MV_RIGHT,
7784       MV_DOWN,
7785       MV_UP,
7786       MV_LEFT,
7787       MV_RIGHT,
7788     };
7789     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7790     int move_preference = -1000000;     /* start with very low preference */
7791     int new_move_dir = MV_NONE;
7792     int start_test = RND(4);
7793     int i;
7794
7795     for (i = 0; i < NUM_DIRECTIONS; i++)
7796     {
7797       int move_dir = test_dir[start_test + i];
7798       int move_dir_preference;
7799
7800       xx = x + test_xy[start_test + i][0];
7801       yy = y + test_xy[start_test + i][1];
7802
7803       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7804           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7805       {
7806         new_move_dir = move_dir;
7807
7808         break;
7809       }
7810
7811       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7812         continue;
7813
7814       move_dir_preference = -1 * RunnerVisit[xx][yy];
7815       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7816         move_dir_preference = PlayerVisit[xx][yy];
7817
7818       if (move_dir_preference > move_preference)
7819       {
7820         /* prefer field that has not been visited for the longest time */
7821         move_preference = move_dir_preference;
7822         new_move_dir = move_dir;
7823       }
7824       else if (move_dir_preference == move_preference &&
7825                move_dir == old_move_dir)
7826       {
7827         /* prefer last direction when all directions are preferred equally */
7828         move_preference = move_dir_preference;
7829         new_move_dir = move_dir;
7830       }
7831     }
7832
7833     MovDir[x][y] = new_move_dir;
7834     if (old_move_dir != new_move_dir)
7835       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7836   }
7837 }
7838
7839 static void TurnRound(int x, int y)
7840 {
7841   int direction = MovDir[x][y];
7842
7843   TurnRoundExt(x, y);
7844
7845   GfxDir[x][y] = MovDir[x][y];
7846
7847   if (direction != MovDir[x][y])
7848     GfxFrame[x][y] = 0;
7849
7850   if (MovDelay[x][y])
7851     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7852
7853   ResetGfxFrame(x, y, FALSE);
7854 }
7855
7856 static boolean JustBeingPushed(int x, int y)
7857 {
7858   int i;
7859
7860   for (i = 0; i < MAX_PLAYERS; i++)
7861   {
7862     struct PlayerInfo *player = &stored_player[i];
7863
7864     if (player->active && player->is_pushing && player->MovPos)
7865     {
7866       int next_jx = player->jx + (player->jx - player->last_jx);
7867       int next_jy = player->jy + (player->jy - player->last_jy);
7868
7869       if (x == next_jx && y == next_jy)
7870         return TRUE;
7871     }
7872   }
7873
7874   return FALSE;
7875 }
7876
7877 void StartMoving(int x, int y)
7878 {
7879   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7880   int element = Feld[x][y];
7881
7882   if (Stop[x][y])
7883     return;
7884
7885   if (MovDelay[x][y] == 0)
7886     GfxAction[x][y] = ACTION_DEFAULT;
7887
7888   if (CAN_FALL(element) && y < lev_fieldy - 1)
7889   {
7890     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7891         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7892       if (JustBeingPushed(x, y))
7893         return;
7894
7895     if (element == EL_QUICKSAND_FULL)
7896     {
7897       if (IS_FREE(x, y + 1))
7898       {
7899         InitMovingField(x, y, MV_DOWN);
7900         started_moving = TRUE;
7901
7902         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7903 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7904         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7905           Store[x][y] = EL_ROCK;
7906 #else
7907         Store[x][y] = EL_ROCK;
7908 #endif
7909
7910         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7911       }
7912       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7913       {
7914         if (!MovDelay[x][y])
7915         {
7916           MovDelay[x][y] = TILEY + 1;
7917
7918           ResetGfxAnimation(x, y);
7919           ResetGfxAnimation(x, y + 1);
7920         }
7921
7922         if (MovDelay[x][y])
7923         {
7924           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7925           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7926
7927           MovDelay[x][y]--;
7928           if (MovDelay[x][y])
7929             return;
7930         }
7931
7932         Feld[x][y] = EL_QUICKSAND_EMPTY;
7933         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7934         Store[x][y + 1] = Store[x][y];
7935         Store[x][y] = 0;
7936
7937         PlayLevelSoundAction(x, y, ACTION_FILLING);
7938       }
7939       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7940       {
7941         if (!MovDelay[x][y])
7942         {
7943           MovDelay[x][y] = TILEY + 1;
7944
7945           ResetGfxAnimation(x, y);
7946           ResetGfxAnimation(x, y + 1);
7947         }
7948
7949         if (MovDelay[x][y])
7950         {
7951           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7953
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Feld[x][y] = EL_QUICKSAND_EMPTY;
7960         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7961         Store[x][y + 1] = Store[x][y];
7962         Store[x][y] = 0;
7963
7964         PlayLevelSoundAction(x, y, ACTION_FILLING);
7965       }
7966     }
7967     else if (element == EL_QUICKSAND_FAST_FULL)
7968     {
7969       if (IS_FREE(x, y + 1))
7970       {
7971         InitMovingField(x, y, MV_DOWN);
7972         started_moving = TRUE;
7973
7974         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7976         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7977           Store[x][y] = EL_ROCK;
7978 #else
7979         Store[x][y] = EL_ROCK;
7980 #endif
7981
7982         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7983       }
7984       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7985       {
7986         if (!MovDelay[x][y])
7987         {
7988           MovDelay[x][y] = TILEY + 1;
7989
7990           ResetGfxAnimation(x, y);
7991           ResetGfxAnimation(x, y + 1);
7992         }
7993
7994         if (MovDelay[x][y])
7995         {
7996           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7997           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7998
7999           MovDelay[x][y]--;
8000           if (MovDelay[x][y])
8001             return;
8002         }
8003
8004         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8005         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8006         Store[x][y + 1] = Store[x][y];
8007         Store[x][y] = 0;
8008
8009         PlayLevelSoundAction(x, y, ACTION_FILLING);
8010       }
8011       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8012       {
8013         if (!MovDelay[x][y])
8014         {
8015           MovDelay[x][y] = TILEY + 1;
8016
8017           ResetGfxAnimation(x, y);
8018           ResetGfxAnimation(x, y + 1);
8019         }
8020
8021         if (MovDelay[x][y])
8022         {
8023           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8025
8026           MovDelay[x][y]--;
8027           if (MovDelay[x][y])
8028             return;
8029         }
8030
8031         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8033         Store[x][y + 1] = Store[x][y];
8034         Store[x][y] = 0;
8035
8036         PlayLevelSoundAction(x, y, ACTION_FILLING);
8037       }
8038     }
8039     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8040              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041     {
8042       InitMovingField(x, y, MV_DOWN);
8043       started_moving = TRUE;
8044
8045       Feld[x][y] = EL_QUICKSAND_FILLING;
8046       Store[x][y] = element;
8047
8048       PlayLevelSoundAction(x, y, ACTION_FILLING);
8049     }
8050     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8051              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8052     {
8053       InitMovingField(x, y, MV_DOWN);
8054       started_moving = TRUE;
8055
8056       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8057       Store[x][y] = element;
8058
8059       PlayLevelSoundAction(x, y, ACTION_FILLING);
8060     }
8061     else if (element == EL_MAGIC_WALL_FULL)
8062     {
8063       if (IS_FREE(x, y + 1))
8064       {
8065         InitMovingField(x, y, MV_DOWN);
8066         started_moving = TRUE;
8067
8068         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8069         Store[x][y] = EL_CHANGED(Store[x][y]);
8070       }
8071       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8072       {
8073         if (!MovDelay[x][y])
8074           MovDelay[x][y] = TILEY/4 + 1;
8075
8076         if (MovDelay[x][y])
8077         {
8078           MovDelay[x][y]--;
8079           if (MovDelay[x][y])
8080             return;
8081         }
8082
8083         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8084         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8085         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8086         Store[x][y] = 0;
8087       }
8088     }
8089     else if (element == EL_BD_MAGIC_WALL_FULL)
8090     {
8091       if (IS_FREE(x, y + 1))
8092       {
8093         InitMovingField(x, y, MV_DOWN);
8094         started_moving = TRUE;
8095
8096         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8097         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8098       }
8099       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8100       {
8101         if (!MovDelay[x][y])
8102           MovDelay[x][y] = TILEY/4 + 1;
8103
8104         if (MovDelay[x][y])
8105         {
8106           MovDelay[x][y]--;
8107           if (MovDelay[x][y])
8108             return;
8109         }
8110
8111         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8112         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8113         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8114         Store[x][y] = 0;
8115       }
8116     }
8117     else if (element == EL_DC_MAGIC_WALL_FULL)
8118     {
8119       if (IS_FREE(x, y + 1))
8120       {
8121         InitMovingField(x, y, MV_DOWN);
8122         started_moving = TRUE;
8123
8124         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8125         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8126       }
8127       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8128       {
8129         if (!MovDelay[x][y])
8130           MovDelay[x][y] = TILEY/4 + 1;
8131
8132         if (MovDelay[x][y])
8133         {
8134           MovDelay[x][y]--;
8135           if (MovDelay[x][y])
8136             return;
8137         }
8138
8139         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8140         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8141         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8142         Store[x][y] = 0;
8143       }
8144     }
8145     else if ((CAN_PASS_MAGIC_WALL(element) &&
8146               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8147                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8148              (CAN_PASS_DC_MAGIC_WALL(element) &&
8149               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8150
8151     {
8152       InitMovingField(x, y, MV_DOWN);
8153       started_moving = TRUE;
8154
8155       Feld[x][y] =
8156         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8157          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8158          EL_DC_MAGIC_WALL_FILLING);
8159       Store[x][y] = element;
8160     }
8161     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8162     {
8163       SplashAcid(x, y + 1);
8164
8165       InitMovingField(x, y, MV_DOWN);
8166       started_moving = TRUE;
8167
8168       Store[x][y] = EL_ACID;
8169     }
8170     else if (
8171 #if USE_FIX_IMPACT_COLLISION
8172              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8174 #else
8175              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8177 #endif
8178              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8179               CAN_FALL(element) && WasJustFalling[x][y] &&
8180               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8181
8182              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8183               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8184               (Feld[x][y + 1] == EL_BLOCKED)))
8185     {
8186       /* this is needed for a special case not covered by calling "Impact()"
8187          from "ContinueMoving()": if an element moves to a tile directly below
8188          another element which was just falling on that tile (which was empty
8189          in the previous frame), the falling element above would just stop
8190          instead of smashing the element below (in previous version, the above
8191          element was just checked for "moving" instead of "falling", resulting
8192          in incorrect smashes caused by horizontal movement of the above
8193          element; also, the case of the player being the element to smash was
8194          simply not covered here... :-/ ) */
8195
8196       CheckCollision[x][y] = 0;
8197       CheckImpact[x][y] = 0;
8198
8199       Impact(x, y);
8200     }
8201     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8202     {
8203       if (MovDir[x][y] == MV_NONE)
8204       {
8205         InitMovingField(x, y, MV_DOWN);
8206         started_moving = TRUE;
8207       }
8208     }
8209     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8210     {
8211       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8212         MovDir[x][y] = MV_DOWN;
8213
8214       InitMovingField(x, y, MV_DOWN);
8215       started_moving = TRUE;
8216     }
8217     else if (element == EL_AMOEBA_DROP)
8218     {
8219       Feld[x][y] = EL_AMOEBA_GROWING;
8220       Store[x][y] = EL_AMOEBA_WET;
8221     }
8222     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8223               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8224              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8225              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8226     {
8227       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8228                                 (IS_FREE(x - 1, y + 1) ||
8229                                  Feld[x - 1][y + 1] == EL_ACID));
8230       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8231                                 (IS_FREE(x + 1, y + 1) ||
8232                                  Feld[x + 1][y + 1] == EL_ACID));
8233       boolean can_fall_any  = (can_fall_left || can_fall_right);
8234       boolean can_fall_both = (can_fall_left && can_fall_right);
8235       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8236
8237 #if USE_NEW_ALL_SLIPPERY
8238       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8239       {
8240         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8241           can_fall_right = FALSE;
8242         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8243           can_fall_left = FALSE;
8244         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8245           can_fall_right = FALSE;
8246         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8247           can_fall_left = FALSE;
8248
8249         can_fall_any  = (can_fall_left || can_fall_right);
8250         can_fall_both = FALSE;
8251       }
8252 #else
8253       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8254       {
8255         if (slippery_type == SLIPPERY_ONLY_LEFT)
8256           can_fall_right = FALSE;
8257         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8258           can_fall_left = FALSE;
8259         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260           can_fall_right = FALSE;
8261         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262           can_fall_left = FALSE;
8263
8264         can_fall_any  = (can_fall_left || can_fall_right);
8265         can_fall_both = (can_fall_left && can_fall_right);
8266       }
8267 #endif
8268
8269 #if USE_NEW_ALL_SLIPPERY
8270 #else
8271 #if USE_NEW_SP_SLIPPERY
8272       /* !!! better use the same properties as for custom elements here !!! */
8273       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8274                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8275       {
8276         can_fall_right = FALSE;         /* slip down on left side */
8277         can_fall_both = FALSE;
8278       }
8279 #endif
8280 #endif
8281
8282 #if USE_NEW_ALL_SLIPPERY
8283       if (can_fall_both)
8284       {
8285         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8286           can_fall_right = FALSE;       /* slip down on left side */
8287         else
8288           can_fall_left = !(can_fall_right = RND(2));
8289
8290         can_fall_both = FALSE;
8291       }
8292 #else
8293       if (can_fall_both)
8294       {
8295         if (game.emulation == EMU_BOULDERDASH ||
8296             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8297           can_fall_right = FALSE;       /* slip down on left side */
8298         else
8299           can_fall_left = !(can_fall_right = RND(2));
8300
8301         can_fall_both = FALSE;
8302       }
8303 #endif
8304
8305       if (can_fall_any)
8306       {
8307         /* if not determined otherwise, prefer left side for slipping down */
8308         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8309         started_moving = TRUE;
8310       }
8311     }
8312 #if 0
8313     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8314 #else
8315     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8316 #endif
8317     {
8318       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8319       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8320       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8321       int belt_dir = game.belt_dir[belt_nr];
8322
8323       if ((belt_dir == MV_LEFT  && left_is_free) ||
8324           (belt_dir == MV_RIGHT && right_is_free))
8325       {
8326         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8327
8328         InitMovingField(x, y, belt_dir);
8329         started_moving = TRUE;
8330
8331         Pushed[x][y] = TRUE;
8332         Pushed[nextx][y] = TRUE;
8333
8334         GfxAction[x][y] = ACTION_DEFAULT;
8335       }
8336       else
8337       {
8338         MovDir[x][y] = 0;       /* if element was moving, stop it */
8339       }
8340     }
8341   }
8342
8343   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8344 #if 0
8345   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8346 #else
8347   if (CAN_MOVE(element) && !started_moving)
8348 #endif
8349   {
8350     int move_pattern = element_info[element].move_pattern;
8351     int newx, newy;
8352
8353 #if 0
8354 #if DEBUG
8355     if (MovDir[x][y] == MV_NONE)
8356     {
8357       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8358              x, y, element, element_info[element].token_name);
8359       printf("StartMoving(): This should never happen!\n");
8360     }
8361 #endif
8362 #endif
8363
8364     Moving2Blocked(x, y, &newx, &newy);
8365
8366     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8367       return;
8368
8369     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8370         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8371     {
8372       WasJustMoving[x][y] = 0;
8373       CheckCollision[x][y] = 0;
8374
8375       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8376
8377       if (Feld[x][y] != element)        /* element has changed */
8378         return;
8379     }
8380
8381     if (!MovDelay[x][y])        /* start new movement phase */
8382     {
8383       /* all objects that can change their move direction after each step
8384          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8385
8386       if (element != EL_YAMYAM &&
8387           element != EL_DARK_YAMYAM &&
8388           element != EL_PACMAN &&
8389           !(move_pattern & MV_ANY_DIRECTION) &&
8390           move_pattern != MV_TURNING_LEFT &&
8391           move_pattern != MV_TURNING_RIGHT &&
8392           move_pattern != MV_TURNING_LEFT_RIGHT &&
8393           move_pattern != MV_TURNING_RIGHT_LEFT &&
8394           move_pattern != MV_TURNING_RANDOM)
8395       {
8396         TurnRound(x, y);
8397
8398         if (MovDelay[x][y] && (element == EL_BUG ||
8399                                element == EL_SPACESHIP ||
8400                                element == EL_SP_SNIKSNAK ||
8401                                element == EL_SP_ELECTRON ||
8402                                element == EL_MOLE))
8403           TEST_DrawLevelField(x, y);
8404       }
8405     }
8406
8407     if (MovDelay[x][y])         /* wait some time before next movement */
8408     {
8409       MovDelay[x][y]--;
8410
8411       if (element == EL_ROBOT ||
8412           element == EL_YAMYAM ||
8413           element == EL_DARK_YAMYAM)
8414       {
8415         DrawLevelElementAnimationIfNeeded(x, y, element);
8416         PlayLevelSoundAction(x, y, ACTION_WAITING);
8417       }
8418       else if (element == EL_SP_ELECTRON)
8419         DrawLevelElementAnimationIfNeeded(x, y, element);
8420       else if (element == EL_DRAGON)
8421       {
8422         int i;
8423         int dir = MovDir[x][y];
8424         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8425         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8426         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8427                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8428                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8429                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8430         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8431
8432         GfxAction[x][y] = ACTION_ATTACKING;
8433
8434         if (IS_PLAYER(x, y))
8435           DrawPlayerField(x, y);
8436         else
8437           TEST_DrawLevelField(x, y);
8438
8439         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8440
8441         for (i = 1; i <= 3; i++)
8442         {
8443           int xx = x + i * dx;
8444           int yy = y + i * dy;
8445           int sx = SCREENX(xx);
8446           int sy = SCREENY(yy);
8447           int flame_graphic = graphic + (i - 1);
8448
8449           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8450             break;
8451
8452           if (MovDelay[x][y])
8453           {
8454             int flamed = MovingOrBlocked2Element(xx, yy);
8455
8456             /* !!! */
8457 #if 0
8458             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8459               Bang(xx, yy);
8460             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8461               RemoveMovingField(xx, yy);
8462             else
8463               RemoveField(xx, yy);
8464 #else
8465             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8466               Bang(xx, yy);
8467             else
8468               RemoveMovingField(xx, yy);
8469 #endif
8470
8471             ChangeDelay[xx][yy] = 0;
8472
8473             Feld[xx][yy] = EL_FLAMES;
8474
8475             if (IN_SCR_FIELD(sx, sy))
8476             {
8477               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8478               DrawGraphic(sx, sy, flame_graphic, frame);
8479             }
8480           }
8481           else
8482           {
8483             if (Feld[xx][yy] == EL_FLAMES)
8484               Feld[xx][yy] = EL_EMPTY;
8485             TEST_DrawLevelField(xx, yy);
8486           }
8487         }
8488       }
8489
8490       if (MovDelay[x][y])       /* element still has to wait some time */
8491       {
8492         PlayLevelSoundAction(x, y, ACTION_WAITING);
8493
8494         return;
8495       }
8496     }
8497
8498     /* now make next step */
8499
8500     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8501
8502     if (DONT_COLLIDE_WITH(element) &&
8503         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8504         !PLAYER_ENEMY_PROTECTED(newx, newy))
8505     {
8506       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8507
8508       return;
8509     }
8510
8511     else if (CAN_MOVE_INTO_ACID(element) &&
8512              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8513              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8514              (MovDir[x][y] == MV_DOWN ||
8515               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8516     {
8517       SplashAcid(newx, newy);
8518       Store[x][y] = EL_ACID;
8519     }
8520     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8521     {
8522       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8523           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8524           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8525           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8526       {
8527         RemoveField(x, y);
8528         TEST_DrawLevelField(x, y);
8529
8530         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8531         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8532           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8533
8534         local_player->friends_still_needed--;
8535         if (!local_player->friends_still_needed &&
8536             !local_player->GameOver && AllPlayersGone)
8537           PlayerWins(local_player);
8538
8539         return;
8540       }
8541       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8542       {
8543         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8544           TEST_DrawLevelField(newx, newy);
8545         else
8546           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8547       }
8548       else if (!IS_FREE(newx, newy))
8549       {
8550         GfxAction[x][y] = ACTION_WAITING;
8551
8552         if (IS_PLAYER(x, y))
8553           DrawPlayerField(x, y);
8554         else
8555           TEST_DrawLevelField(x, y);
8556
8557         return;
8558       }
8559     }
8560     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8561     {
8562       if (IS_FOOD_PIG(Feld[newx][newy]))
8563       {
8564         if (IS_MOVING(newx, newy))
8565           RemoveMovingField(newx, newy);
8566         else
8567         {
8568           Feld[newx][newy] = EL_EMPTY;
8569           TEST_DrawLevelField(newx, newy);
8570         }
8571
8572         PlayLevelSound(x, y, SND_PIG_DIGGING);
8573       }
8574       else if (!IS_FREE(newx, newy))
8575       {
8576         if (IS_PLAYER(x, y))
8577           DrawPlayerField(x, y);
8578         else
8579           TEST_DrawLevelField(x, y);
8580
8581         return;
8582       }
8583     }
8584     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8585     {
8586       if (Store[x][y] != EL_EMPTY)
8587       {
8588         boolean can_clone = FALSE;
8589         int xx, yy;
8590
8591         /* check if element to clone is still there */
8592         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8593         {
8594           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8595           {
8596             can_clone = TRUE;
8597
8598             break;
8599           }
8600         }
8601
8602         /* cannot clone or target field not free anymore -- do not clone */
8603         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8604           Store[x][y] = EL_EMPTY;
8605       }
8606
8607       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8608       {
8609         if (IS_MV_DIAGONAL(MovDir[x][y]))
8610         {
8611           int diagonal_move_dir = MovDir[x][y];
8612           int stored = Store[x][y];
8613           int change_delay = 8;
8614           int graphic;
8615
8616           /* android is moving diagonally */
8617
8618           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8619
8620           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8621           GfxElement[x][y] = EL_EMC_ANDROID;
8622           GfxAction[x][y] = ACTION_SHRINKING;
8623           GfxDir[x][y] = diagonal_move_dir;
8624           ChangeDelay[x][y] = change_delay;
8625
8626           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8627                                    GfxDir[x][y]);
8628
8629           DrawLevelGraphicAnimation(x, y, graphic);
8630           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8631
8632           if (Feld[newx][newy] == EL_ACID)
8633           {
8634             SplashAcid(newx, newy);
8635
8636             return;
8637           }
8638
8639           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8640
8641           Store[newx][newy] = EL_EMC_ANDROID;
8642           GfxElement[newx][newy] = EL_EMC_ANDROID;
8643           GfxAction[newx][newy] = ACTION_GROWING;
8644           GfxDir[newx][newy] = diagonal_move_dir;
8645           ChangeDelay[newx][newy] = change_delay;
8646
8647           graphic = el_act_dir2img(GfxElement[newx][newy],
8648                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8649
8650           DrawLevelGraphicAnimation(newx, newy, graphic);
8651           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8652
8653           return;
8654         }
8655         else
8656         {
8657           Feld[newx][newy] = EL_EMPTY;
8658           TEST_DrawLevelField(newx, newy);
8659
8660           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8661         }
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665 #if 0
8666         if (IS_PLAYER(x, y))
8667           DrawPlayerField(x, y);
8668         else
8669           TEST_DrawLevelField(x, y);
8670 #endif
8671
8672         return;
8673       }
8674     }
8675     else if (IS_CUSTOM_ELEMENT(element) &&
8676              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8677     {
8678 #if 1
8679       if (!DigFieldByCE(newx, newy, element))
8680         return;
8681 #else
8682       int new_element = Feld[newx][newy];
8683
8684       if (!IS_FREE(newx, newy))
8685       {
8686         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8687                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8688                       ACTION_BREAKING);
8689
8690         /* no element can dig solid indestructible elements */
8691         if (IS_INDESTRUCTIBLE(new_element) &&
8692             !IS_DIGGABLE(new_element) &&
8693             !IS_COLLECTIBLE(new_element))
8694           return;
8695
8696         if (AmoebaNr[newx][newy] &&
8697             (new_element == EL_AMOEBA_FULL ||
8698              new_element == EL_BD_AMOEBA ||
8699              new_element == EL_AMOEBA_GROWING))
8700         {
8701           AmoebaCnt[AmoebaNr[newx][newy]]--;
8702           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8703         }
8704
8705         if (IS_MOVING(newx, newy))
8706           RemoveMovingField(newx, newy);
8707         else
8708         {
8709           RemoveField(newx, newy);
8710           TEST_DrawLevelField(newx, newy);
8711         }
8712
8713         /* if digged element was about to explode, prevent the explosion */
8714         ExplodeField[newx][newy] = EX_TYPE_NONE;
8715
8716         PlayLevelSoundAction(x, y, action);
8717       }
8718
8719       Store[newx][newy] = EL_EMPTY;
8720
8721 #if 1
8722       /* this makes it possible to leave the removed element again */
8723       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8724         Store[newx][newy] = new_element;
8725 #else
8726       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8727       {
8728         int move_leave_element = element_info[element].move_leave_element;
8729
8730         /* this makes it possible to leave the removed element again */
8731         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8732                              new_element : move_leave_element);
8733       }
8734 #endif
8735
8736 #endif
8737
8738       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8739       {
8740         RunnerVisit[x][y] = FrameCounter;
8741         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8742       }
8743     }
8744     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8745     {
8746       if (!IS_FREE(newx, newy))
8747       {
8748         if (IS_PLAYER(x, y))
8749           DrawPlayerField(x, y);
8750         else
8751           TEST_DrawLevelField(x, y);
8752
8753         return;
8754       }
8755       else
8756       {
8757         boolean wanna_flame = !RND(10);
8758         int dx = newx - x, dy = newy - y;
8759         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8760         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8761         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8762                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8763         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8764                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8765
8766         if ((wanna_flame ||
8767              IS_CLASSIC_ENEMY(element1) ||
8768              IS_CLASSIC_ENEMY(element2)) &&
8769             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8770             element1 != EL_FLAMES && element2 != EL_FLAMES)
8771         {
8772           ResetGfxAnimation(x, y);
8773           GfxAction[x][y] = ACTION_ATTACKING;
8774
8775           if (IS_PLAYER(x, y))
8776             DrawPlayerField(x, y);
8777           else
8778             TEST_DrawLevelField(x, y);
8779
8780           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8781
8782           MovDelay[x][y] = 50;
8783
8784           /* !!! */
8785 #if 0
8786           RemoveField(newx, newy);
8787 #endif
8788           Feld[newx][newy] = EL_FLAMES;
8789           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8790           {
8791 #if 0
8792             RemoveField(newx1, newy1);
8793 #endif
8794             Feld[newx1][newy1] = EL_FLAMES;
8795           }
8796           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8797           {
8798 #if 0
8799             RemoveField(newx2, newy2);
8800 #endif
8801             Feld[newx2][newy2] = EL_FLAMES;
8802           }
8803
8804           return;
8805         }
8806       }
8807     }
8808     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8809              Feld[newx][newy] == EL_DIAMOND)
8810     {
8811       if (IS_MOVING(newx, newy))
8812         RemoveMovingField(newx, newy);
8813       else
8814       {
8815         Feld[newx][newy] = EL_EMPTY;
8816         TEST_DrawLevelField(newx, newy);
8817       }
8818
8819       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8820     }
8821     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8822              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8823     {
8824       if (AmoebaNr[newx][newy])
8825       {
8826         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8827         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8828             Feld[newx][newy] == EL_BD_AMOEBA)
8829           AmoebaCnt[AmoebaNr[newx][newy]]--;
8830       }
8831
8832 #if 0
8833       /* !!! test !!! */
8834       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8835       {
8836         RemoveMovingField(newx, newy);
8837       }
8838 #else
8839       if (IS_MOVING(newx, newy))
8840       {
8841         RemoveMovingField(newx, newy);
8842       }
8843 #endif
8844       else
8845       {
8846         Feld[newx][newy] = EL_EMPTY;
8847         TEST_DrawLevelField(newx, newy);
8848       }
8849
8850       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8851     }
8852     else if ((element == EL_PACMAN || element == EL_MOLE)
8853              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8854     {
8855       if (AmoebaNr[newx][newy])
8856       {
8857         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8858         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8859             Feld[newx][newy] == EL_BD_AMOEBA)
8860           AmoebaCnt[AmoebaNr[newx][newy]]--;
8861       }
8862
8863       if (element == EL_MOLE)
8864       {
8865         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8866         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8867
8868         ResetGfxAnimation(x, y);
8869         GfxAction[x][y] = ACTION_DIGGING;
8870         TEST_DrawLevelField(x, y);
8871
8872         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8873
8874         return;                         /* wait for shrinking amoeba */
8875       }
8876       else      /* element == EL_PACMAN */
8877       {
8878         Feld[newx][newy] = EL_EMPTY;
8879         TEST_DrawLevelField(newx, newy);
8880         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8881       }
8882     }
8883     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8884              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8885               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8886     {
8887       /* wait for shrinking amoeba to completely disappear */
8888       return;
8889     }
8890     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8891     {
8892       /* object was running against a wall */
8893
8894       TurnRound(x, y);
8895
8896 #if 0
8897       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8898       if (move_pattern & MV_ANY_DIRECTION &&
8899           move_pattern == MovDir[x][y])
8900       {
8901         int blocking_element =
8902           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8903
8904         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8905                                  MovDir[x][y]);
8906
8907         element = Feld[x][y];   /* element might have changed */
8908       }
8909 #endif
8910
8911       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8912         DrawLevelElementAnimation(x, y, element);
8913
8914       if (DONT_TOUCH(element))
8915         TestIfBadThingTouchesPlayer(x, y);
8916
8917       return;
8918     }
8919
8920     InitMovingField(x, y, MovDir[x][y]);
8921
8922     PlayLevelSoundAction(x, y, ACTION_MOVING);
8923   }
8924
8925   if (MovDir[x][y])
8926     ContinueMoving(x, y);
8927 }
8928
8929 void ContinueMoving(int x, int y)
8930 {
8931   int element = Feld[x][y];
8932   struct ElementInfo *ei = &element_info[element];
8933   int direction = MovDir[x][y];
8934   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8935   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8936   int newx = x + dx, newy = y + dy;
8937   int stored = Store[x][y];
8938   int stored_new = Store[newx][newy];
8939   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8940   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8941   boolean last_line = (newy == lev_fieldy - 1);
8942
8943   MovPos[x][y] += getElementMoveStepsize(x, y);
8944
8945   if (pushed_by_player) /* special case: moving object pushed by player */
8946     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8947
8948   if (ABS(MovPos[x][y]) < TILEX)
8949   {
8950 #if 0
8951     int ee = Feld[x][y];
8952     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8953     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8954
8955     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8956            x, y, ABS(MovPos[x][y]),
8957            ee, gg, ff,
8958            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8959 #endif
8960
8961     TEST_DrawLevelField(x, y);
8962
8963     return;     /* element is still moving */
8964   }
8965
8966   /* element reached destination field */
8967
8968   Feld[x][y] = EL_EMPTY;
8969   Feld[newx][newy] = element;
8970   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8971
8972   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8973   {
8974     element = Feld[newx][newy] = EL_ACID;
8975   }
8976   else if (element == EL_MOLE)
8977   {
8978     Feld[x][y] = EL_SAND;
8979
8980     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8981   }
8982   else if (element == EL_QUICKSAND_FILLING)
8983   {
8984     element = Feld[newx][newy] = get_next_element(element);
8985     Store[newx][newy] = Store[x][y];
8986   }
8987   else if (element == EL_QUICKSAND_EMPTYING)
8988   {
8989     Feld[x][y] = get_next_element(element);
8990     element = Feld[newx][newy] = Store[x][y];
8991   }
8992   else if (element == EL_QUICKSAND_FAST_FILLING)
8993   {
8994     element = Feld[newx][newy] = get_next_element(element);
8995     Store[newx][newy] = Store[x][y];
8996   }
8997   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8998   {
8999     Feld[x][y] = get_next_element(element);
9000     element = Feld[newx][newy] = Store[x][y];
9001   }
9002   else if (element == EL_MAGIC_WALL_FILLING)
9003   {
9004     element = Feld[newx][newy] = get_next_element(element);
9005     if (!game.magic_wall_active)
9006       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9007     Store[newx][newy] = Store[x][y];
9008   }
9009   else if (element == EL_MAGIC_WALL_EMPTYING)
9010   {
9011     Feld[x][y] = get_next_element(element);
9012     if (!game.magic_wall_active)
9013       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9014     element = Feld[newx][newy] = Store[x][y];
9015
9016 #if USE_NEW_CUSTOM_VALUE
9017     InitField(newx, newy, FALSE);
9018 #endif
9019   }
9020   else if (element == EL_BD_MAGIC_WALL_FILLING)
9021   {
9022     element = Feld[newx][newy] = get_next_element(element);
9023     if (!game.magic_wall_active)
9024       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9025     Store[newx][newy] = Store[x][y];
9026   }
9027   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9028   {
9029     Feld[x][y] = get_next_element(element);
9030     if (!game.magic_wall_active)
9031       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9032     element = Feld[newx][newy] = Store[x][y];
9033
9034 #if USE_NEW_CUSTOM_VALUE
9035     InitField(newx, newy, FALSE);
9036 #endif
9037   }
9038   else if (element == EL_DC_MAGIC_WALL_FILLING)
9039   {
9040     element = Feld[newx][newy] = get_next_element(element);
9041     if (!game.magic_wall_active)
9042       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9043     Store[newx][newy] = Store[x][y];
9044   }
9045   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9046   {
9047     Feld[x][y] = get_next_element(element);
9048     if (!game.magic_wall_active)
9049       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9050     element = Feld[newx][newy] = Store[x][y];
9051
9052 #if USE_NEW_CUSTOM_VALUE
9053     InitField(newx, newy, FALSE);
9054 #endif
9055   }
9056   else if (element == EL_AMOEBA_DROPPING)
9057   {
9058     Feld[x][y] = get_next_element(element);
9059     element = Feld[newx][newy] = Store[x][y];
9060   }
9061   else if (element == EL_SOKOBAN_OBJECT)
9062   {
9063     if (Back[x][y])
9064       Feld[x][y] = Back[x][y];
9065
9066     if (Back[newx][newy])
9067       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9068
9069     Back[x][y] = Back[newx][newy] = 0;
9070   }
9071
9072   Store[x][y] = EL_EMPTY;
9073   MovPos[x][y] = 0;
9074   MovDir[x][y] = 0;
9075   MovDelay[x][y] = 0;
9076
9077   MovDelay[newx][newy] = 0;
9078
9079   if (CAN_CHANGE_OR_HAS_ACTION(element))
9080   {
9081     /* copy element change control values to new field */
9082     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9083     ChangePage[newx][newy]  = ChangePage[x][y];
9084     ChangeCount[newx][newy] = ChangeCount[x][y];
9085     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9086   }
9087
9088 #if USE_NEW_CUSTOM_VALUE
9089   CustomValue[newx][newy] = CustomValue[x][y];
9090 #endif
9091
9092   ChangeDelay[x][y] = 0;
9093   ChangePage[x][y] = -1;
9094   ChangeCount[x][y] = 0;
9095   ChangeEvent[x][y] = -1;
9096
9097 #if USE_NEW_CUSTOM_VALUE
9098   CustomValue[x][y] = 0;
9099 #endif
9100
9101   /* copy animation control values to new field */
9102   GfxFrame[newx][newy]  = GfxFrame[x][y];
9103   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9104   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9105   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9106
9107   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9108
9109   /* some elements can leave other elements behind after moving */
9110 #if 1
9111   if (ei->move_leave_element != EL_EMPTY &&
9112       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9113       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9114 #else
9115   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9116       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9117       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9118 #endif
9119   {
9120     int move_leave_element = ei->move_leave_element;
9121
9122 #if 1
9123 #if 1
9124     /* this makes it possible to leave the removed element again */
9125     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9126       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9127 #else
9128     /* this makes it possible to leave the removed element again */
9129     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9130       move_leave_element = stored;
9131 #endif
9132 #else
9133     /* this makes it possible to leave the removed element again */
9134     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9135         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9136       move_leave_element = stored;
9137 #endif
9138
9139     Feld[x][y] = move_leave_element;
9140
9141     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9142       MovDir[x][y] = direction;
9143
9144     InitField(x, y, FALSE);
9145
9146     if (GFX_CRUMBLED(Feld[x][y]))
9147       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9148
9149     if (ELEM_IS_PLAYER(move_leave_element))
9150       RelocatePlayer(x, y, move_leave_element);
9151   }
9152
9153   /* do this after checking for left-behind element */
9154   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9155
9156   if (!CAN_MOVE(element) ||
9157       (CAN_FALL(element) && direction == MV_DOWN &&
9158        (element == EL_SPRING ||
9159         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9160         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9161     GfxDir[x][y] = MovDir[newx][newy] = 0;
9162
9163   TEST_DrawLevelField(x, y);
9164   TEST_DrawLevelField(newx, newy);
9165
9166   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9167
9168   /* prevent pushed element from moving on in pushed direction */
9169   if (pushed_by_player && CAN_MOVE(element) &&
9170       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9171       !(element_info[element].move_pattern & direction))
9172     TurnRound(newx, newy);
9173
9174   /* prevent elements on conveyor belt from moving on in last direction */
9175   if (pushed_by_conveyor && CAN_FALL(element) &&
9176       direction & MV_HORIZONTAL)
9177     MovDir[newx][newy] = 0;
9178
9179   if (!pushed_by_player)
9180   {
9181     int nextx = newx + dx, nexty = newy + dy;
9182     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9183
9184     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9185
9186     if (CAN_FALL(element) && direction == MV_DOWN)
9187       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9188
9189     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9190       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9191
9192 #if USE_FIX_IMPACT_COLLISION
9193     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9194       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9195 #endif
9196   }
9197
9198   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9199   {
9200     TestIfBadThingTouchesPlayer(newx, newy);
9201     TestIfBadThingTouchesFriend(newx, newy);
9202
9203     if (!IS_CUSTOM_ELEMENT(element))
9204       TestIfBadThingTouchesOtherBadThing(newx, newy);
9205   }
9206   else if (element == EL_PENGUIN)
9207     TestIfFriendTouchesBadThing(newx, newy);
9208
9209   if (DONT_GET_HIT_BY(element))
9210   {
9211     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9212   }
9213
9214   /* give the player one last chance (one more frame) to move away */
9215   if (CAN_FALL(element) && direction == MV_DOWN &&
9216       (last_line || (!IS_FREE(x, newy + 1) &&
9217                      (!IS_PLAYER(x, newy + 1) ||
9218                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9219     Impact(x, newy);
9220
9221   if (pushed_by_player && !game.use_change_when_pushing_bug)
9222   {
9223     int push_side = MV_DIR_OPPOSITE(direction);
9224     struct PlayerInfo *player = PLAYERINFO(x, y);
9225
9226     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9227                                player->index_bit, push_side);
9228     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9229                                         player->index_bit, push_side);
9230   }
9231
9232   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9233     MovDelay[newx][newy] = 1;
9234
9235   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9236
9237   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9238
9239 #if 0
9240   if (ChangePage[newx][newy] != -1)             /* delayed change */
9241   {
9242     int page = ChangePage[newx][newy];
9243     struct ElementChangeInfo *change = &ei->change_page[page];
9244
9245     ChangePage[newx][newy] = -1;
9246
9247     if (change->can_change)
9248     {
9249       if (ChangeElement(newx, newy, element, page))
9250       {
9251         if (change->post_change_function)
9252           change->post_change_function(newx, newy);
9253       }
9254     }
9255
9256     if (change->has_action)
9257       ExecuteCustomElementAction(newx, newy, element, page);
9258   }
9259 #endif
9260
9261   TestIfElementHitsCustomElement(newx, newy, direction);
9262   TestIfPlayerTouchesCustomElement(newx, newy);
9263   TestIfElementTouchesCustomElement(newx, newy);
9264
9265   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9266       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9267     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9268                              MV_DIR_OPPOSITE(direction));
9269 }
9270
9271 int AmoebeNachbarNr(int ax, int ay)
9272 {
9273   int i;
9274   int element = Feld[ax][ay];
9275   int group_nr = 0;
9276   static int xy[4][2] =
9277   {
9278     { 0, -1 },
9279     { -1, 0 },
9280     { +1, 0 },
9281     { 0, +1 }
9282   };
9283
9284   for (i = 0; i < NUM_DIRECTIONS; i++)
9285   {
9286     int x = ax + xy[i][0];
9287     int y = ay + xy[i][1];
9288
9289     if (!IN_LEV_FIELD(x, y))
9290       continue;
9291
9292     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9293       group_nr = AmoebaNr[x][y];
9294   }
9295
9296   return group_nr;
9297 }
9298
9299 void AmoebenVereinigen(int ax, int ay)
9300 {
9301   int i, x, y, xx, yy;
9302   int new_group_nr = AmoebaNr[ax][ay];
9303   static int xy[4][2] =
9304   {
9305     { 0, -1 },
9306     { -1, 0 },
9307     { +1, 0 },
9308     { 0, +1 }
9309   };
9310
9311   if (new_group_nr == 0)
9312     return;
9313
9314   for (i = 0; i < NUM_DIRECTIONS; i++)
9315   {
9316     x = ax + xy[i][0];
9317     y = ay + xy[i][1];
9318
9319     if (!IN_LEV_FIELD(x, y))
9320       continue;
9321
9322     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9323          Feld[x][y] == EL_BD_AMOEBA ||
9324          Feld[x][y] == EL_AMOEBA_DEAD) &&
9325         AmoebaNr[x][y] != new_group_nr)
9326     {
9327       int old_group_nr = AmoebaNr[x][y];
9328
9329       if (old_group_nr == 0)
9330         return;
9331
9332       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9333       AmoebaCnt[old_group_nr] = 0;
9334       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9335       AmoebaCnt2[old_group_nr] = 0;
9336
9337       SCAN_PLAYFIELD(xx, yy)
9338       {
9339         if (AmoebaNr[xx][yy] == old_group_nr)
9340           AmoebaNr[xx][yy] = new_group_nr;
9341       }
9342     }
9343   }
9344 }
9345
9346 void AmoebeUmwandeln(int ax, int ay)
9347 {
9348   int i, x, y;
9349
9350   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9351   {
9352     int group_nr = AmoebaNr[ax][ay];
9353
9354 #ifdef DEBUG
9355     if (group_nr == 0)
9356     {
9357       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9358       printf("AmoebeUmwandeln(): This should never happen!\n");
9359       return;
9360     }
9361 #endif
9362
9363     SCAN_PLAYFIELD(x, y)
9364     {
9365       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9366       {
9367         AmoebaNr[x][y] = 0;
9368         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9369       }
9370     }
9371
9372     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9373                             SND_AMOEBA_TURNING_TO_GEM :
9374                             SND_AMOEBA_TURNING_TO_ROCK));
9375     Bang(ax, ay);
9376   }
9377   else
9378   {
9379     static int xy[4][2] =
9380     {
9381       { 0, -1 },
9382       { -1, 0 },
9383       { +1, 0 },
9384       { 0, +1 }
9385     };
9386
9387     for (i = 0; i < NUM_DIRECTIONS; i++)
9388     {
9389       x = ax + xy[i][0];
9390       y = ay + xy[i][1];
9391
9392       if (!IN_LEV_FIELD(x, y))
9393         continue;
9394
9395       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9396       {
9397         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9398                               SND_AMOEBA_TURNING_TO_GEM :
9399                               SND_AMOEBA_TURNING_TO_ROCK));
9400         Bang(x, y);
9401       }
9402     }
9403   }
9404 }
9405
9406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9407 {
9408   int x, y;
9409   int group_nr = AmoebaNr[ax][ay];
9410   boolean done = FALSE;
9411
9412 #ifdef DEBUG
9413   if (group_nr == 0)
9414   {
9415     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9416     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9417     return;
9418   }
9419 #endif
9420
9421   SCAN_PLAYFIELD(x, y)
9422   {
9423     if (AmoebaNr[x][y] == group_nr &&
9424         (Feld[x][y] == EL_AMOEBA_DEAD ||
9425          Feld[x][y] == EL_BD_AMOEBA ||
9426          Feld[x][y] == EL_AMOEBA_GROWING))
9427     {
9428       AmoebaNr[x][y] = 0;
9429       Feld[x][y] = new_element;
9430       InitField(x, y, FALSE);
9431       TEST_DrawLevelField(x, y);
9432       done = TRUE;
9433     }
9434   }
9435
9436   if (done)
9437     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9438                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9439                             SND_BD_AMOEBA_TURNING_TO_GEM));
9440 }
9441
9442 void AmoebeWaechst(int x, int y)
9443 {
9444   static unsigned long sound_delay = 0;
9445   static unsigned long sound_delay_value = 0;
9446
9447   if (!MovDelay[x][y])          /* start new growing cycle */
9448   {
9449     MovDelay[x][y] = 7;
9450
9451     if (DelayReached(&sound_delay, sound_delay_value))
9452     {
9453       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9454       sound_delay_value = 30;
9455     }
9456   }
9457
9458   if (MovDelay[x][y])           /* wait some time before growing bigger */
9459   {
9460     MovDelay[x][y]--;
9461     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9462     {
9463       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9464                                            6 - MovDelay[x][y]);
9465
9466       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9467     }
9468
9469     if (!MovDelay[x][y])
9470     {
9471       Feld[x][y] = Store[x][y];
9472       Store[x][y] = 0;
9473       TEST_DrawLevelField(x, y);
9474     }
9475   }
9476 }
9477
9478 void AmoebaDisappearing(int x, int y)
9479 {
9480   static unsigned long sound_delay = 0;
9481   static unsigned long sound_delay_value = 0;
9482
9483   if (!MovDelay[x][y])          /* start new shrinking cycle */
9484   {
9485     MovDelay[x][y] = 7;
9486
9487     if (DelayReached(&sound_delay, sound_delay_value))
9488       sound_delay_value = 30;
9489   }
9490
9491   if (MovDelay[x][y])           /* wait some time before shrinking */
9492   {
9493     MovDelay[x][y]--;
9494     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9495     {
9496       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9497                                            6 - MovDelay[x][y]);
9498
9499       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9500     }
9501
9502     if (!MovDelay[x][y])
9503     {
9504       Feld[x][y] = EL_EMPTY;
9505       TEST_DrawLevelField(x, y);
9506
9507       /* don't let mole enter this field in this cycle;
9508          (give priority to objects falling to this field from above) */
9509       Stop[x][y] = TRUE;
9510     }
9511   }
9512 }
9513
9514 void AmoebeAbleger(int ax, int ay)
9515 {
9516   int i;
9517   int element = Feld[ax][ay];
9518   int graphic = el2img(element);
9519   int newax = ax, neway = ay;
9520   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9521   static int xy[4][2] =
9522   {
9523     { 0, -1 },
9524     { -1, 0 },
9525     { +1, 0 },
9526     { 0, +1 }
9527   };
9528
9529   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9530   {
9531     Feld[ax][ay] = EL_AMOEBA_DEAD;
9532     TEST_DrawLevelField(ax, ay);
9533     return;
9534   }
9535
9536   if (IS_ANIMATED(graphic))
9537     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9538
9539   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9540     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9541
9542   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9543   {
9544     MovDelay[ax][ay]--;
9545     if (MovDelay[ax][ay])
9546       return;
9547   }
9548
9549   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9550   {
9551     int start = RND(4);
9552     int x = ax + xy[start][0];
9553     int y = ay + xy[start][1];
9554
9555     if (!IN_LEV_FIELD(x, y))
9556       return;
9557
9558     if (IS_FREE(x, y) ||
9559         CAN_GROW_INTO(Feld[x][y]) ||
9560         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9561         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9562     {
9563       newax = x;
9564       neway = y;
9565     }
9566
9567     if (newax == ax && neway == ay)
9568       return;
9569   }
9570   else                          /* normal or "filled" (BD style) amoeba */
9571   {
9572     int start = RND(4);
9573     boolean waiting_for_player = FALSE;
9574
9575     for (i = 0; i < NUM_DIRECTIONS; i++)
9576     {
9577       int j = (start + i) % 4;
9578       int x = ax + xy[j][0];
9579       int y = ay + xy[j][1];
9580
9581       if (!IN_LEV_FIELD(x, y))
9582         continue;
9583
9584       if (IS_FREE(x, y) ||
9585           CAN_GROW_INTO(Feld[x][y]) ||
9586           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9587           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9588       {
9589         newax = x;
9590         neway = y;
9591         break;
9592       }
9593       else if (IS_PLAYER(x, y))
9594         waiting_for_player = TRUE;
9595     }
9596
9597     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9598     {
9599       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9600       {
9601         Feld[ax][ay] = EL_AMOEBA_DEAD;
9602         TEST_DrawLevelField(ax, ay);
9603         AmoebaCnt[AmoebaNr[ax][ay]]--;
9604
9605         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9606         {
9607           if (element == EL_AMOEBA_FULL)
9608             AmoebeUmwandeln(ax, ay);
9609           else if (element == EL_BD_AMOEBA)
9610             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9611         }
9612       }
9613       return;
9614     }
9615     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9616     {
9617       /* amoeba gets larger by growing in some direction */
9618
9619       int new_group_nr = AmoebaNr[ax][ay];
9620
9621 #ifdef DEBUG
9622   if (new_group_nr == 0)
9623   {
9624     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9625     printf("AmoebeAbleger(): This should never happen!\n");
9626     return;
9627   }
9628 #endif
9629
9630       AmoebaNr[newax][neway] = new_group_nr;
9631       AmoebaCnt[new_group_nr]++;
9632       AmoebaCnt2[new_group_nr]++;
9633
9634       /* if amoeba touches other amoeba(s) after growing, unify them */
9635       AmoebenVereinigen(newax, neway);
9636
9637       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9638       {
9639         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9640         return;
9641       }
9642     }
9643   }
9644
9645   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9646       (neway == lev_fieldy - 1 && newax != ax))
9647   {
9648     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9649     Store[newax][neway] = element;
9650   }
9651   else if (neway == ay || element == EL_EMC_DRIPPER)
9652   {
9653     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9654
9655     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9656   }
9657   else
9658   {
9659     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9660     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9661     Store[ax][ay] = EL_AMOEBA_DROP;
9662     ContinueMoving(ax, ay);
9663     return;
9664   }
9665
9666   TEST_DrawLevelField(newax, neway);
9667 }
9668
9669 void Life(int ax, int ay)
9670 {
9671   int x1, y1, x2, y2;
9672   int life_time = 40;
9673   int element = Feld[ax][ay];
9674   int graphic = el2img(element);
9675   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9676                          level.biomaze);
9677   boolean changed = FALSE;
9678
9679   if (IS_ANIMATED(graphic))
9680     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9681
9682   if (Stop[ax][ay])
9683     return;
9684
9685   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9686     MovDelay[ax][ay] = life_time;
9687
9688   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9689   {
9690     MovDelay[ax][ay]--;
9691     if (MovDelay[ax][ay])
9692       return;
9693   }
9694
9695   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9696   {
9697     int xx = ax+x1, yy = ay+y1;
9698     int nachbarn = 0;
9699
9700     if (!IN_LEV_FIELD(xx, yy))
9701       continue;
9702
9703     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9704     {
9705       int x = xx+x2, y = yy+y2;
9706
9707       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9708         continue;
9709
9710       if (((Feld[x][y] == element ||
9711             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9712            !Stop[x][y]) ||
9713           (IS_FREE(x, y) && Stop[x][y]))
9714         nachbarn++;
9715     }
9716
9717     if (xx == ax && yy == ay)           /* field in the middle */
9718     {
9719       if (nachbarn < life_parameter[0] ||
9720           nachbarn > life_parameter[1])
9721       {
9722         Feld[xx][yy] = EL_EMPTY;
9723         if (!Stop[xx][yy])
9724           TEST_DrawLevelField(xx, yy);
9725         Stop[xx][yy] = TRUE;
9726         changed = TRUE;
9727       }
9728     }
9729     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9730     {                                   /* free border field */
9731       if (nachbarn >= life_parameter[2] &&
9732           nachbarn <= life_parameter[3])
9733       {
9734         Feld[xx][yy] = element;
9735         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9736         if (!Stop[xx][yy])
9737           TEST_DrawLevelField(xx, yy);
9738         Stop[xx][yy] = TRUE;
9739         changed = TRUE;
9740       }
9741     }
9742   }
9743
9744   if (changed)
9745     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9746                    SND_GAME_OF_LIFE_GROWING);
9747 }
9748
9749 static void InitRobotWheel(int x, int y)
9750 {
9751   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9752 }
9753
9754 static void RunRobotWheel(int x, int y)
9755 {
9756   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9757 }
9758
9759 static void StopRobotWheel(int x, int y)
9760 {
9761   if (ZX == x && ZY == y)
9762   {
9763     ZX = ZY = -1;
9764
9765     game.robot_wheel_active = FALSE;
9766   }
9767 }
9768
9769 static void InitTimegateWheel(int x, int y)
9770 {
9771   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9772 }
9773
9774 static void RunTimegateWheel(int x, int y)
9775 {
9776   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9777 }
9778
9779 static void InitMagicBallDelay(int x, int y)
9780 {
9781 #if 1
9782   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9783 #else
9784   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9785 #endif
9786 }
9787
9788 static void ActivateMagicBall(int bx, int by)
9789 {
9790   int x, y;
9791
9792   if (level.ball_random)
9793   {
9794     int pos_border = RND(8);    /* select one of the eight border elements */
9795     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9796     int xx = pos_content % 3;
9797     int yy = pos_content / 3;
9798
9799     x = bx - 1 + xx;
9800     y = by - 1 + yy;
9801
9802     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9803       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9804   }
9805   else
9806   {
9807     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9808     {
9809       int xx = x - bx + 1;
9810       int yy = y - by + 1;
9811
9812       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9813         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9814     }
9815   }
9816
9817   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9818 }
9819
9820 void CheckExit(int x, int y)
9821 {
9822   if (local_player->gems_still_needed > 0 ||
9823       local_player->sokobanfields_still_needed > 0 ||
9824       local_player->lights_still_needed > 0)
9825   {
9826     int element = Feld[x][y];
9827     int graphic = el2img(element);
9828
9829     if (IS_ANIMATED(graphic))
9830       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9831
9832     return;
9833   }
9834
9835   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9836     return;
9837
9838   Feld[x][y] = EL_EXIT_OPENING;
9839
9840   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9841 }
9842
9843 void CheckExitEM(int x, int y)
9844 {
9845   if (local_player->gems_still_needed > 0 ||
9846       local_player->sokobanfields_still_needed > 0 ||
9847       local_player->lights_still_needed > 0)
9848   {
9849     int element = Feld[x][y];
9850     int graphic = el2img(element);
9851
9852     if (IS_ANIMATED(graphic))
9853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9854
9855     return;
9856   }
9857
9858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9859     return;
9860
9861   Feld[x][y] = EL_EM_EXIT_OPENING;
9862
9863   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9864 }
9865
9866 void CheckExitSteel(int x, int y)
9867 {
9868   if (local_player->gems_still_needed > 0 ||
9869       local_player->sokobanfields_still_needed > 0 ||
9870       local_player->lights_still_needed > 0)
9871   {
9872     int element = Feld[x][y];
9873     int graphic = el2img(element);
9874
9875     if (IS_ANIMATED(graphic))
9876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9877
9878     return;
9879   }
9880
9881   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9882     return;
9883
9884   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9885
9886   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9887 }
9888
9889 void CheckExitSteelEM(int x, int y)
9890 {
9891   if (local_player->gems_still_needed > 0 ||
9892       local_player->sokobanfields_still_needed > 0 ||
9893       local_player->lights_still_needed > 0)
9894   {
9895     int element = Feld[x][y];
9896     int graphic = el2img(element);
9897
9898     if (IS_ANIMATED(graphic))
9899       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9900
9901     return;
9902   }
9903
9904   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9905     return;
9906
9907   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9908
9909   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9910 }
9911
9912 void CheckExitSP(int x, int y)
9913 {
9914   if (local_player->gems_still_needed > 0)
9915   {
9916     int element = Feld[x][y];
9917     int graphic = el2img(element);
9918
9919     if (IS_ANIMATED(graphic))
9920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921
9922     return;
9923   }
9924
9925   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9926     return;
9927
9928   Feld[x][y] = EL_SP_EXIT_OPENING;
9929
9930   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9931 }
9932
9933 static void CloseAllOpenTimegates()
9934 {
9935   int x, y;
9936
9937   SCAN_PLAYFIELD(x, y)
9938   {
9939     int element = Feld[x][y];
9940
9941     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9942     {
9943       Feld[x][y] = EL_TIMEGATE_CLOSING;
9944
9945       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9946     }
9947   }
9948 }
9949
9950 void DrawTwinkleOnField(int x, int y)
9951 {
9952   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9953     return;
9954
9955   if (Feld[x][y] == EL_BD_DIAMOND)
9956     return;
9957
9958   if (MovDelay[x][y] == 0)      /* next animation frame */
9959     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9960
9961   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9962   {
9963     MovDelay[x][y]--;
9964
9965     DrawLevelElementAnimation(x, y, Feld[x][y]);
9966
9967     if (MovDelay[x][y] != 0)
9968     {
9969       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9970                                            10 - MovDelay[x][y]);
9971
9972       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9973     }
9974   }
9975 }
9976
9977 void MauerWaechst(int x, int y)
9978 {
9979   int delay = 6;
9980
9981   if (!MovDelay[x][y])          /* next animation frame */
9982     MovDelay[x][y] = 3 * delay;
9983
9984   if (MovDelay[x][y])           /* wait some time before next frame */
9985   {
9986     MovDelay[x][y]--;
9987
9988     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9989     {
9990       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9991       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9992
9993       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9994     }
9995
9996     if (!MovDelay[x][y])
9997     {
9998       if (MovDir[x][y] == MV_LEFT)
9999       {
10000         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10001           TEST_DrawLevelField(x - 1, y);
10002       }
10003       else if (MovDir[x][y] == MV_RIGHT)
10004       {
10005         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10006           TEST_DrawLevelField(x + 1, y);
10007       }
10008       else if (MovDir[x][y] == MV_UP)
10009       {
10010         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10011           TEST_DrawLevelField(x, y - 1);
10012       }
10013       else
10014       {
10015         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10016           TEST_DrawLevelField(x, y + 1);
10017       }
10018
10019       Feld[x][y] = Store[x][y];
10020       Store[x][y] = 0;
10021       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10022       TEST_DrawLevelField(x, y);
10023     }
10024   }
10025 }
10026
10027 void MauerAbleger(int ax, int ay)
10028 {
10029   int element = Feld[ax][ay];
10030   int graphic = el2img(element);
10031   boolean oben_frei = FALSE, unten_frei = FALSE;
10032   boolean links_frei = FALSE, rechts_frei = FALSE;
10033   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10034   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10035   boolean new_wall = FALSE;
10036
10037   if (IS_ANIMATED(graphic))
10038     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10039
10040   if (!MovDelay[ax][ay])        /* start building new wall */
10041     MovDelay[ax][ay] = 6;
10042
10043   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10044   {
10045     MovDelay[ax][ay]--;
10046     if (MovDelay[ax][ay])
10047       return;
10048   }
10049
10050   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10051     oben_frei = TRUE;
10052   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10053     unten_frei = TRUE;
10054   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10055     links_frei = TRUE;
10056   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10057     rechts_frei = TRUE;
10058
10059   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10060       element == EL_EXPANDABLE_WALL_ANY)
10061   {
10062     if (oben_frei)
10063     {
10064       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10065       Store[ax][ay-1] = element;
10066       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10067       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10068         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10069                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10070       new_wall = TRUE;
10071     }
10072     if (unten_frei)
10073     {
10074       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10075       Store[ax][ay+1] = element;
10076       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10078         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10079                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10080       new_wall = TRUE;
10081     }
10082   }
10083
10084   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10085       element == EL_EXPANDABLE_WALL_ANY ||
10086       element == EL_EXPANDABLE_WALL ||
10087       element == EL_BD_EXPANDABLE_WALL)
10088   {
10089     if (links_frei)
10090     {
10091       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10092       Store[ax-1][ay] = element;
10093       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10094       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10095         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10096                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10097       new_wall = TRUE;
10098     }
10099
10100     if (rechts_frei)
10101     {
10102       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10103       Store[ax+1][ay] = element;
10104       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10105       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10106         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10107                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10108       new_wall = TRUE;
10109     }
10110   }
10111
10112   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10113     TEST_DrawLevelField(ax, ay);
10114
10115   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10116     oben_massiv = TRUE;
10117   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10118     unten_massiv = TRUE;
10119   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10120     links_massiv = TRUE;
10121   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10122     rechts_massiv = TRUE;
10123
10124   if (((oben_massiv && unten_massiv) ||
10125        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126        element == EL_EXPANDABLE_WALL) &&
10127       ((links_massiv && rechts_massiv) ||
10128        element == EL_EXPANDABLE_WALL_VERTICAL))
10129     Feld[ax][ay] = EL_WALL;
10130
10131   if (new_wall)
10132     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10133 }
10134
10135 void MauerAblegerStahl(int ax, int ay)
10136 {
10137   int element = Feld[ax][ay];
10138   int graphic = el2img(element);
10139   boolean oben_frei = FALSE, unten_frei = FALSE;
10140   boolean links_frei = FALSE, rechts_frei = FALSE;
10141   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10142   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10143   boolean new_wall = FALSE;
10144
10145   if (IS_ANIMATED(graphic))
10146     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10147
10148   if (!MovDelay[ax][ay])        /* start building new wall */
10149     MovDelay[ax][ay] = 6;
10150
10151   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10152   {
10153     MovDelay[ax][ay]--;
10154     if (MovDelay[ax][ay])
10155       return;
10156   }
10157
10158   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10159     oben_frei = TRUE;
10160   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10161     unten_frei = TRUE;
10162   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10163     links_frei = TRUE;
10164   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10165     rechts_frei = TRUE;
10166
10167   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10168       element == EL_EXPANDABLE_STEELWALL_ANY)
10169   {
10170     if (oben_frei)
10171     {
10172       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10173       Store[ax][ay-1] = element;
10174       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10175       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10176         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10177                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10178       new_wall = TRUE;
10179     }
10180     if (unten_frei)
10181     {
10182       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10183       Store[ax][ay+1] = element;
10184       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10185       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10186         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10187                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10188       new_wall = TRUE;
10189     }
10190   }
10191
10192   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10193       element == EL_EXPANDABLE_STEELWALL_ANY)
10194   {
10195     if (links_frei)
10196     {
10197       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10198       Store[ax-1][ay] = element;
10199       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10200       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10201         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10202                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10203       new_wall = TRUE;
10204     }
10205
10206     if (rechts_frei)
10207     {
10208       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10209       Store[ax+1][ay] = element;
10210       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10211       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10212         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10213                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10214       new_wall = TRUE;
10215     }
10216   }
10217
10218   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10219     oben_massiv = TRUE;
10220   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10221     unten_massiv = TRUE;
10222   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10223     links_massiv = TRUE;
10224   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10225     rechts_massiv = TRUE;
10226
10227   if (((oben_massiv && unten_massiv) ||
10228        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10229       ((links_massiv && rechts_massiv) ||
10230        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10231     Feld[ax][ay] = EL_STEELWALL;
10232
10233   if (new_wall)
10234     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10235 }
10236
10237 void CheckForDragon(int x, int y)
10238 {
10239   int i, j;
10240   boolean dragon_found = FALSE;
10241   static int xy[4][2] =
10242   {
10243     { 0, -1 },
10244     { -1, 0 },
10245     { +1, 0 },
10246     { 0, +1 }
10247   };
10248
10249   for (i = 0; i < NUM_DIRECTIONS; i++)
10250   {
10251     for (j = 0; j < 4; j++)
10252     {
10253       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10254
10255       if (IN_LEV_FIELD(xx, yy) &&
10256           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10257       {
10258         if (Feld[xx][yy] == EL_DRAGON)
10259           dragon_found = TRUE;
10260       }
10261       else
10262         break;
10263     }
10264   }
10265
10266   if (!dragon_found)
10267   {
10268     for (i = 0; i < NUM_DIRECTIONS; i++)
10269     {
10270       for (j = 0; j < 3; j++)
10271       {
10272         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10273   
10274         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10275         {
10276           Feld[xx][yy] = EL_EMPTY;
10277           TEST_DrawLevelField(xx, yy);
10278         }
10279         else
10280           break;
10281       }
10282     }
10283   }
10284 }
10285
10286 static void InitBuggyBase(int x, int y)
10287 {
10288   int element = Feld[x][y];
10289   int activating_delay = FRAMES_PER_SECOND / 4;
10290
10291   ChangeDelay[x][y] =
10292     (element == EL_SP_BUGGY_BASE ?
10293      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10294      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10295      activating_delay :
10296      element == EL_SP_BUGGY_BASE_ACTIVE ?
10297      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10298 }
10299
10300 static void WarnBuggyBase(int x, int y)
10301 {
10302   int i;
10303   static int xy[4][2] =
10304   {
10305     { 0, -1 },
10306     { -1, 0 },
10307     { +1, 0 },
10308     { 0, +1 }
10309   };
10310
10311   for (i = 0; i < NUM_DIRECTIONS; i++)
10312   {
10313     int xx = x + xy[i][0];
10314     int yy = y + xy[i][1];
10315
10316     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10317     {
10318       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10319
10320       break;
10321     }
10322   }
10323 }
10324
10325 static void InitTrap(int x, int y)
10326 {
10327   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10328 }
10329
10330 static void ActivateTrap(int x, int y)
10331 {
10332   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10333 }
10334
10335 static void ChangeActiveTrap(int x, int y)
10336 {
10337   int graphic = IMG_TRAP_ACTIVE;
10338
10339   /* if new animation frame was drawn, correct crumbled sand border */
10340   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10341     TEST_DrawLevelFieldCrumbledSand(x, y);
10342 }
10343
10344 static int getSpecialActionElement(int element, int number, int base_element)
10345 {
10346   return (element != EL_EMPTY ? element :
10347           number != -1 ? base_element + number - 1 :
10348           EL_EMPTY);
10349 }
10350
10351 static int getModifiedActionNumber(int value_old, int operator, int operand,
10352                                    int value_min, int value_max)
10353 {
10354   int value_new = (operator == CA_MODE_SET      ? operand :
10355                    operator == CA_MODE_ADD      ? value_old + operand :
10356                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10357                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10358                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10359                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10360                    value_old);
10361
10362   return (value_new < value_min ? value_min :
10363           value_new > value_max ? value_max :
10364           value_new);
10365 }
10366
10367 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10368 {
10369   struct ElementInfo *ei = &element_info[element];
10370   struct ElementChangeInfo *change = &ei->change_page[page];
10371   int target_element = change->target_element;
10372   int action_type = change->action_type;
10373   int action_mode = change->action_mode;
10374   int action_arg = change->action_arg;
10375   int action_element = change->action_element;
10376   int i;
10377
10378   if (!change->has_action)
10379     return;
10380
10381   /* ---------- determine action paramater values -------------------------- */
10382
10383   int level_time_value =
10384     (level.time > 0 ? TimeLeft :
10385      TimePlayed);
10386
10387   int action_arg_element_raw =
10388     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10389      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10390      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10391      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10392      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10393      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10394      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10395      EL_EMPTY);
10396   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10397
10398 #if 0
10399   if (action_arg_element_raw == EL_GROUP_START)
10400     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10401 #endif
10402
10403   int action_arg_direction =
10404     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10405      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10406      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10407      change->actual_trigger_side :
10408      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10409      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10410      MV_NONE);
10411
10412   int action_arg_number_min =
10413     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10414      CA_ARG_MIN);
10415
10416   int action_arg_number_max =
10417     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10418      action_type == CA_SET_LEVEL_GEMS ? 999 :
10419      action_type == CA_SET_LEVEL_TIME ? 9999 :
10420      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10421      action_type == CA_SET_CE_VALUE ? 9999 :
10422      action_type == CA_SET_CE_SCORE ? 9999 :
10423      CA_ARG_MAX);
10424
10425   int action_arg_number_reset =
10426     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10427      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10428      action_type == CA_SET_LEVEL_TIME ? level.time :
10429      action_type == CA_SET_LEVEL_SCORE ? 0 :
10430 #if USE_NEW_CUSTOM_VALUE
10431      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10432 #else
10433      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10434 #endif
10435      action_type == CA_SET_CE_SCORE ? 0 :
10436      0);
10437
10438   int action_arg_number =
10439     (action_arg <= CA_ARG_MAX ? action_arg :
10440      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10441      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10442      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10443      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10444      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10445      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10446 #if USE_NEW_CUSTOM_VALUE
10447      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10448 #else
10449      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10450 #endif
10451      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10452      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10453      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10454      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10455      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10456      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10457      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10458      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10459      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10460      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10461      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10462      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10463      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10464      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10465      -1);
10466
10467   int action_arg_number_old =
10468     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10469      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10470      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10471      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10472      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10473      0);
10474
10475   int action_arg_number_new =
10476     getModifiedActionNumber(action_arg_number_old,
10477                             action_mode, action_arg_number,
10478                             action_arg_number_min, action_arg_number_max);
10479
10480 #if 1
10481   int trigger_player_bits =
10482     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10483      change->actual_trigger_player_bits : change->trigger_player);
10484 #else
10485   int trigger_player_bits =
10486     (change->actual_trigger_player >= EL_PLAYER_1 &&
10487      change->actual_trigger_player <= EL_PLAYER_4 ?
10488      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10489      PLAYER_BITS_ANY);
10490 #endif
10491
10492   int action_arg_player_bits =
10493     (action_arg >= CA_ARG_PLAYER_1 &&
10494      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10495      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10496      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10497      PLAYER_BITS_ANY);
10498
10499   /* ---------- execute action  -------------------------------------------- */
10500
10501   switch (action_type)
10502   {
10503     case CA_NO_ACTION:
10504     {
10505       return;
10506     }
10507
10508     /* ---------- level actions  ------------------------------------------- */
10509
10510     case CA_RESTART_LEVEL:
10511     {
10512       game.restart_level = TRUE;
10513
10514       break;
10515     }
10516
10517     case CA_SHOW_ENVELOPE:
10518     {
10519       int element = getSpecialActionElement(action_arg_element,
10520                                             action_arg_number, EL_ENVELOPE_1);
10521
10522       if (IS_ENVELOPE(element))
10523         local_player->show_envelope = element;
10524
10525       break;
10526     }
10527
10528     case CA_SET_LEVEL_TIME:
10529     {
10530       if (level.time > 0)       /* only modify limited time value */
10531       {
10532         TimeLeft = action_arg_number_new;
10533
10534 #if 1
10535         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10536
10537         DisplayGameControlValues();
10538 #else
10539         DrawGameValue_Time(TimeLeft);
10540 #endif
10541
10542         if (!TimeLeft && setup.time_limit)
10543           for (i = 0; i < MAX_PLAYERS; i++)
10544             KillPlayer(&stored_player[i]);
10545       }
10546
10547       break;
10548     }
10549
10550     case CA_SET_LEVEL_SCORE:
10551     {
10552       local_player->score = action_arg_number_new;
10553
10554 #if 1
10555       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10556
10557       DisplayGameControlValues();
10558 #else
10559       DrawGameValue_Score(local_player->score);
10560 #endif
10561
10562       break;
10563     }
10564
10565     case CA_SET_LEVEL_GEMS:
10566     {
10567       local_player->gems_still_needed = action_arg_number_new;
10568
10569 #if 1
10570       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10571
10572       DisplayGameControlValues();
10573 #else
10574       DrawGameValue_Emeralds(local_player->gems_still_needed);
10575 #endif
10576
10577       break;
10578     }
10579
10580 #if !USE_PLAYER_GRAVITY
10581     case CA_SET_LEVEL_GRAVITY:
10582     {
10583       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10584                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10585                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10586                       game.gravity);
10587       break;
10588     }
10589 #endif
10590
10591     case CA_SET_LEVEL_WIND:
10592     {
10593       game.wind_direction = action_arg_direction;
10594
10595       break;
10596     }
10597
10598     case CA_SET_LEVEL_RANDOM_SEED:
10599     {
10600 #if 1
10601       /* ensure that setting a new random seed while playing is predictable */
10602       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10603 #else
10604       InitRND(action_arg_number_new);
10605 #endif
10606
10607 #if 0
10608       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10609 #endif
10610
10611 #if 0
10612       {
10613         int i;
10614
10615         printf("::: ");
10616         for (i = 0; i < 9; i++)
10617           printf("%d, ", RND(2));
10618         printf("\n");
10619       }
10620 #endif
10621
10622       break;
10623     }
10624
10625     /* ---------- player actions  ------------------------------------------ */
10626
10627     case CA_MOVE_PLAYER:
10628     {
10629       /* automatically move to the next field in specified direction */
10630       for (i = 0; i < MAX_PLAYERS; i++)
10631         if (trigger_player_bits & (1 << i))
10632           stored_player[i].programmed_action = action_arg_direction;
10633
10634       break;
10635     }
10636
10637     case CA_EXIT_PLAYER:
10638     {
10639       for (i = 0; i < MAX_PLAYERS; i++)
10640         if (action_arg_player_bits & (1 << i))
10641           PlayerWins(&stored_player[i]);
10642
10643       break;
10644     }
10645
10646     case CA_KILL_PLAYER:
10647     {
10648       for (i = 0; i < MAX_PLAYERS; i++)
10649         if (action_arg_player_bits & (1 << i))
10650           KillPlayer(&stored_player[i]);
10651
10652       break;
10653     }
10654
10655     case CA_SET_PLAYER_KEYS:
10656     {
10657       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10658       int element = getSpecialActionElement(action_arg_element,
10659                                             action_arg_number, EL_KEY_1);
10660
10661       if (IS_KEY(element))
10662       {
10663         for (i = 0; i < MAX_PLAYERS; i++)
10664         {
10665           if (trigger_player_bits & (1 << i))
10666           {
10667             stored_player[i].key[KEY_NR(element)] = key_state;
10668
10669             DrawGameDoorValues();
10670           }
10671         }
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_PLAYER_SPEED:
10678     {
10679 #if 0
10680       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10681 #endif
10682
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684       {
10685         if (trigger_player_bits & (1 << i))
10686         {
10687           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10688
10689           if (action_arg == CA_ARG_SPEED_FASTER &&
10690               stored_player[i].cannot_move)
10691           {
10692             action_arg_number = STEPSIZE_VERY_SLOW;
10693           }
10694           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10695                    action_arg == CA_ARG_SPEED_FASTER)
10696           {
10697             action_arg_number = 2;
10698             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10699                            CA_MODE_MULTIPLY);
10700           }
10701           else if (action_arg == CA_ARG_NUMBER_RESET)
10702           {
10703             action_arg_number = level.initial_player_stepsize[i];
10704           }
10705
10706           move_stepsize =
10707             getModifiedActionNumber(move_stepsize,
10708                                     action_mode,
10709                                     action_arg_number,
10710                                     action_arg_number_min,
10711                                     action_arg_number_max);
10712
10713           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10714         }
10715       }
10716
10717       break;
10718     }
10719
10720     case CA_SET_PLAYER_SHIELD:
10721     {
10722       for (i = 0; i < MAX_PLAYERS; i++)
10723       {
10724         if (trigger_player_bits & (1 << i))
10725         {
10726           if (action_arg == CA_ARG_SHIELD_OFF)
10727           {
10728             stored_player[i].shield_normal_time_left = 0;
10729             stored_player[i].shield_deadly_time_left = 0;
10730           }
10731           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10732           {
10733             stored_player[i].shield_normal_time_left = 999999;
10734           }
10735           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10736           {
10737             stored_player[i].shield_normal_time_left = 999999;
10738             stored_player[i].shield_deadly_time_left = 999999;
10739           }
10740         }
10741       }
10742
10743       break;
10744     }
10745
10746 #if USE_PLAYER_GRAVITY
10747     case CA_SET_PLAYER_GRAVITY:
10748     {
10749       for (i = 0; i < MAX_PLAYERS; i++)
10750       {
10751         if (trigger_player_bits & (1 << i))
10752         {
10753           stored_player[i].gravity =
10754             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10755              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10756              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10757              stored_player[i].gravity);
10758         }
10759       }
10760
10761       break;
10762     }
10763 #endif
10764
10765     case CA_SET_PLAYER_ARTWORK:
10766     {
10767       for (i = 0; i < MAX_PLAYERS; i++)
10768       {
10769         if (trigger_player_bits & (1 << i))
10770         {
10771           int artwork_element = action_arg_element;
10772
10773           if (action_arg == CA_ARG_ELEMENT_RESET)
10774             artwork_element =
10775               (level.use_artwork_element[i] ? level.artwork_element[i] :
10776                stored_player[i].element_nr);
10777
10778 #if USE_GFX_RESET_PLAYER_ARTWORK
10779           if (stored_player[i].artwork_element != artwork_element)
10780             stored_player[i].Frame = 0;
10781 #endif
10782
10783           stored_player[i].artwork_element = artwork_element;
10784
10785           SetPlayerWaiting(&stored_player[i], FALSE);
10786
10787           /* set number of special actions for bored and sleeping animation */
10788           stored_player[i].num_special_action_bored =
10789             get_num_special_action(artwork_element,
10790                                    ACTION_BORING_1, ACTION_BORING_LAST);
10791           stored_player[i].num_special_action_sleeping =
10792             get_num_special_action(artwork_element,
10793                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10794         }
10795       }
10796
10797       break;
10798     }
10799
10800     case CA_SET_PLAYER_INVENTORY:
10801     {
10802       for (i = 0; i < MAX_PLAYERS; i++)
10803       {
10804         struct PlayerInfo *player = &stored_player[i];
10805         int j, k;
10806
10807         if (trigger_player_bits & (1 << i))
10808         {
10809           int inventory_element = action_arg_element;
10810
10811           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10812               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10813               action_arg == CA_ARG_ELEMENT_ACTION)
10814           {
10815             int element = inventory_element;
10816             int collect_count = element_info[element].collect_count_initial;
10817
10818             if (!IS_CUSTOM_ELEMENT(element))
10819               collect_count = 1;
10820
10821             if (collect_count == 0)
10822               player->inventory_infinite_element = element;
10823             else
10824               for (k = 0; k < collect_count; k++)
10825                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10826                   player->inventory_element[player->inventory_size++] =
10827                     element;
10828           }
10829           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10830                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10831                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10832           {
10833             if (player->inventory_infinite_element != EL_UNDEFINED &&
10834                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10835                                      action_arg_element_raw))
10836               player->inventory_infinite_element = EL_UNDEFINED;
10837
10838             for (k = 0, j = 0; j < player->inventory_size; j++)
10839             {
10840               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10841                                         action_arg_element_raw))
10842                 player->inventory_element[k++] = player->inventory_element[j];
10843             }
10844
10845             player->inventory_size = k;
10846           }
10847           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10848           {
10849             if (player->inventory_size > 0)
10850             {
10851               for (j = 0; j < player->inventory_size - 1; j++)
10852                 player->inventory_element[j] = player->inventory_element[j + 1];
10853
10854               player->inventory_size--;
10855             }
10856           }
10857           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10858           {
10859             if (player->inventory_size > 0)
10860               player->inventory_size--;
10861           }
10862           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10863           {
10864             player->inventory_infinite_element = EL_UNDEFINED;
10865             player->inventory_size = 0;
10866           }
10867           else if (action_arg == CA_ARG_INVENTORY_RESET)
10868           {
10869             player->inventory_infinite_element = EL_UNDEFINED;
10870             player->inventory_size = 0;
10871
10872             if (level.use_initial_inventory[i])
10873             {
10874               for (j = 0; j < level.initial_inventory_size[i]; j++)
10875               {
10876                 int element = level.initial_inventory_content[i][j];
10877                 int collect_count = element_info[element].collect_count_initial;
10878
10879                 if (!IS_CUSTOM_ELEMENT(element))
10880                   collect_count = 1;
10881
10882                 if (collect_count == 0)
10883                   player->inventory_infinite_element = element;
10884                 else
10885                   for (k = 0; k < collect_count; k++)
10886                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10887                       player->inventory_element[player->inventory_size++] =
10888                         element;
10889               }
10890             }
10891           }
10892         }
10893       }
10894
10895       break;
10896     }
10897
10898     /* ---------- CE actions  ---------------------------------------------- */
10899
10900     case CA_SET_CE_VALUE:
10901     {
10902 #if USE_NEW_CUSTOM_VALUE
10903       int last_ce_value = CustomValue[x][y];
10904
10905       CustomValue[x][y] = action_arg_number_new;
10906
10907       if (CustomValue[x][y] != last_ce_value)
10908       {
10909         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10910         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10911
10912         if (CustomValue[x][y] == 0)
10913         {
10914           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10915           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10916         }
10917       }
10918 #endif
10919
10920       break;
10921     }
10922
10923     case CA_SET_CE_SCORE:
10924     {
10925 #if USE_NEW_CUSTOM_VALUE
10926       int last_ce_score = ei->collect_score;
10927
10928       ei->collect_score = action_arg_number_new;
10929
10930       if (ei->collect_score != last_ce_score)
10931       {
10932         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10933         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10934
10935         if (ei->collect_score == 0)
10936         {
10937           int xx, yy;
10938
10939           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10940           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10941
10942           /*
10943             This is a very special case that seems to be a mixture between
10944             CheckElementChange() and CheckTriggeredElementChange(): while
10945             the first one only affects single elements that are triggered
10946             directly, the second one affects multiple elements in the playfield
10947             that are triggered indirectly by another element. This is a third
10948             case: Changing the CE score always affects multiple identical CEs,
10949             so every affected CE must be checked, not only the single CE for
10950             which the CE score was changed in the first place (as every instance
10951             of that CE shares the same CE score, and therefore also can change)!
10952           */
10953           SCAN_PLAYFIELD(xx, yy)
10954           {
10955             if (Feld[xx][yy] == element)
10956               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10957                                  CE_SCORE_GETS_ZERO);
10958           }
10959         }
10960       }
10961 #endif
10962
10963       break;
10964     }
10965
10966     case CA_SET_CE_ARTWORK:
10967     {
10968       int artwork_element = action_arg_element;
10969       boolean reset_frame = FALSE;
10970       int xx, yy;
10971
10972       if (action_arg == CA_ARG_ELEMENT_RESET)
10973         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10974                            element);
10975
10976       if (ei->gfx_element != artwork_element)
10977         reset_frame = TRUE;
10978
10979       ei->gfx_element = artwork_element;
10980
10981       SCAN_PLAYFIELD(xx, yy)
10982       {
10983         if (Feld[xx][yy] == element)
10984         {
10985           if (reset_frame)
10986           {
10987             ResetGfxAnimation(xx, yy);
10988             ResetRandomAnimationValue(xx, yy);
10989           }
10990
10991           TEST_DrawLevelField(xx, yy);
10992         }
10993       }
10994
10995       break;
10996     }
10997
10998     /* ---------- engine actions  ------------------------------------------ */
10999
11000     case CA_SET_ENGINE_SCAN_MODE:
11001     {
11002       InitPlayfieldScanMode(action_arg);
11003
11004       break;
11005     }
11006
11007     default:
11008       break;
11009   }
11010 }
11011
11012 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11013 {
11014   int old_element = Feld[x][y];
11015   int new_element = GetElementFromGroupElement(element);
11016   int previous_move_direction = MovDir[x][y];
11017 #if USE_NEW_CUSTOM_VALUE
11018   int last_ce_value = CustomValue[x][y];
11019 #endif
11020   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11021   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11022   boolean add_player_onto_element = (new_element_is_player &&
11023 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11024                                      /* this breaks SnakeBite when a snake is
11025                                         halfway through a door that closes */
11026                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11027                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11028 #endif
11029                                      IS_WALKABLE(old_element));
11030
11031 #if 0
11032   /* check if element under the player changes from accessible to unaccessible
11033      (needed for special case of dropping element which then changes) */
11034   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11035       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11036   {
11037     Bang(x, y);
11038
11039     return;
11040   }
11041 #endif
11042
11043   if (!add_player_onto_element)
11044   {
11045     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11046       RemoveMovingField(x, y);
11047     else
11048       RemoveField(x, y);
11049
11050     Feld[x][y] = new_element;
11051
11052 #if !USE_GFX_RESET_GFX_ANIMATION
11053     ResetGfxAnimation(x, y);
11054     ResetRandomAnimationValue(x, y);
11055 #endif
11056
11057     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11058       MovDir[x][y] = previous_move_direction;
11059
11060 #if USE_NEW_CUSTOM_VALUE
11061     if (element_info[new_element].use_last_ce_value)
11062       CustomValue[x][y] = last_ce_value;
11063 #endif
11064
11065     InitField_WithBug1(x, y, FALSE);
11066
11067     new_element = Feld[x][y];   /* element may have changed */
11068
11069 #if USE_GFX_RESET_GFX_ANIMATION
11070     ResetGfxAnimation(x, y);
11071     ResetRandomAnimationValue(x, y);
11072 #endif
11073
11074     TEST_DrawLevelField(x, y);
11075
11076     if (GFX_CRUMBLED(new_element))
11077       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11078   }
11079
11080 #if 1
11081   /* check if element under the player changes from accessible to unaccessible
11082      (needed for special case of dropping element which then changes) */
11083   /* (must be checked after creating new element for walkable group elements) */
11084 #if USE_FIX_KILLED_BY_NON_WALKABLE
11085   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11086       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11087   {
11088     Bang(x, y);
11089
11090     return;
11091   }
11092 #else
11093   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11094       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11095   {
11096     Bang(x, y);
11097
11098     return;
11099   }
11100 #endif
11101 #endif
11102
11103   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11104   if (new_element_is_player)
11105     RelocatePlayer(x, y, new_element);
11106
11107   if (is_change)
11108     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11109
11110   TestIfBadThingTouchesPlayer(x, y);
11111   TestIfPlayerTouchesCustomElement(x, y);
11112   TestIfElementTouchesCustomElement(x, y);
11113 }
11114
11115 static void CreateField(int x, int y, int element)
11116 {
11117   CreateFieldExt(x, y, element, FALSE);
11118 }
11119
11120 static void CreateElementFromChange(int x, int y, int element)
11121 {
11122   element = GET_VALID_RUNTIME_ELEMENT(element);
11123
11124 #if USE_STOP_CHANGED_ELEMENTS
11125   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11126   {
11127     int old_element = Feld[x][y];
11128
11129     /* prevent changed element from moving in same engine frame
11130        unless both old and new element can either fall or move */
11131     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11132         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11133       Stop[x][y] = TRUE;
11134   }
11135 #endif
11136
11137   CreateFieldExt(x, y, element, TRUE);
11138 }
11139
11140 static boolean ChangeElement(int x, int y, int element, int page)
11141 {
11142   struct ElementInfo *ei = &element_info[element];
11143   struct ElementChangeInfo *change = &ei->change_page[page];
11144   int ce_value = CustomValue[x][y];
11145   int ce_score = ei->collect_score;
11146   int target_element;
11147   int old_element = Feld[x][y];
11148
11149   /* always use default change event to prevent running into a loop */
11150   if (ChangeEvent[x][y] == -1)
11151     ChangeEvent[x][y] = CE_DELAY;
11152
11153   if (ChangeEvent[x][y] == CE_DELAY)
11154   {
11155     /* reset actual trigger element, trigger player and action element */
11156     change->actual_trigger_element = EL_EMPTY;
11157     change->actual_trigger_player = EL_EMPTY;
11158     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11159     change->actual_trigger_side = CH_SIDE_NONE;
11160     change->actual_trigger_ce_value = 0;
11161     change->actual_trigger_ce_score = 0;
11162   }
11163
11164   /* do not change elements more than a specified maximum number of changes */
11165   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11166     return FALSE;
11167
11168   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11169
11170   if (change->explode)
11171   {
11172     Bang(x, y);
11173
11174     return TRUE;
11175   }
11176
11177   if (change->use_target_content)
11178   {
11179     boolean complete_replace = TRUE;
11180     boolean can_replace[3][3];
11181     int xx, yy;
11182
11183     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11184     {
11185       boolean is_empty;
11186       boolean is_walkable;
11187       boolean is_diggable;
11188       boolean is_collectible;
11189       boolean is_removable;
11190       boolean is_destructible;
11191       int ex = x + xx - 1;
11192       int ey = y + yy - 1;
11193       int content_element = change->target_content.e[xx][yy];
11194       int e;
11195
11196       can_replace[xx][yy] = TRUE;
11197
11198       if (ex == x && ey == y)   /* do not check changing element itself */
11199         continue;
11200
11201       if (content_element == EL_EMPTY_SPACE)
11202       {
11203         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11204
11205         continue;
11206       }
11207
11208       if (!IN_LEV_FIELD(ex, ey))
11209       {
11210         can_replace[xx][yy] = FALSE;
11211         complete_replace = FALSE;
11212
11213         continue;
11214       }
11215
11216       e = Feld[ex][ey];
11217
11218       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11219         e = MovingOrBlocked2Element(ex, ey);
11220
11221       is_empty = (IS_FREE(ex, ey) ||
11222                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11223
11224       is_walkable     = (is_empty || IS_WALKABLE(e));
11225       is_diggable     = (is_empty || IS_DIGGABLE(e));
11226       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11227       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11228       is_removable    = (is_diggable || is_collectible);
11229
11230       can_replace[xx][yy] =
11231         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11232           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11233           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11234           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11235           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11236           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11237          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11238
11239       if (!can_replace[xx][yy])
11240         complete_replace = FALSE;
11241     }
11242
11243     if (!change->only_if_complete || complete_replace)
11244     {
11245       boolean something_has_changed = FALSE;
11246
11247       if (change->only_if_complete && change->use_random_replace &&
11248           RND(100) < change->random_percentage)
11249         return FALSE;
11250
11251       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11252       {
11253         int ex = x + xx - 1;
11254         int ey = y + yy - 1;
11255         int content_element;
11256
11257         if (can_replace[xx][yy] && (!change->use_random_replace ||
11258                                     RND(100) < change->random_percentage))
11259         {
11260           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11261             RemoveMovingField(ex, ey);
11262
11263           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11264
11265           content_element = change->target_content.e[xx][yy];
11266           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11267                                               ce_value, ce_score);
11268
11269           CreateElementFromChange(ex, ey, target_element);
11270
11271           something_has_changed = TRUE;
11272
11273           /* for symmetry reasons, freeze newly created border elements */
11274           if (ex != x || ey != y)
11275             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11276         }
11277       }
11278
11279       if (something_has_changed)
11280       {
11281         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11282         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11283       }
11284     }
11285   }
11286   else
11287   {
11288     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11289                                         ce_value, ce_score);
11290
11291     if (element == EL_DIAGONAL_GROWING ||
11292         element == EL_DIAGONAL_SHRINKING)
11293     {
11294       target_element = Store[x][y];
11295
11296       Store[x][y] = EL_EMPTY;
11297     }
11298
11299     CreateElementFromChange(x, y, target_element);
11300
11301     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11302     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11303   }
11304
11305   /* this uses direct change before indirect change */
11306   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11307
11308   return TRUE;
11309 }
11310
11311 #if USE_NEW_DELAYED_ACTION
11312
11313 static void HandleElementChange(int x, int y, int page)
11314 {
11315   int element = MovingOrBlocked2Element(x, y);
11316   struct ElementInfo *ei = &element_info[element];
11317   struct ElementChangeInfo *change = &ei->change_page[page];
11318   boolean handle_action_before_change = FALSE;
11319
11320 #ifdef DEBUG
11321   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11322       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11323   {
11324     printf("\n\n");
11325     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11326            x, y, element, element_info[element].token_name);
11327     printf("HandleElementChange(): This should never happen!\n");
11328     printf("\n\n");
11329   }
11330 #endif
11331
11332   /* this can happen with classic bombs on walkable, changing elements */
11333   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11334   {
11335 #if 0
11336     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11337       ChangeDelay[x][y] = 0;
11338 #endif
11339
11340     return;
11341   }
11342
11343   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11344   {
11345     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11346
11347     if (change->can_change)
11348     {
11349 #if 1
11350       /* !!! not clear why graphic animation should be reset at all here !!! */
11351       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11352 #if USE_GFX_RESET_WHEN_NOT_MOVING
11353       /* when a custom element is about to change (for example by change delay),
11354          do not reset graphic animation when the custom element is moving */
11355       if (!IS_MOVING(x, y))
11356 #endif
11357       {
11358         ResetGfxAnimation(x, y);
11359         ResetRandomAnimationValue(x, y);
11360       }
11361 #endif
11362
11363       if (change->pre_change_function)
11364         change->pre_change_function(x, y);
11365     }
11366   }
11367
11368   ChangeDelay[x][y]--;
11369
11370   if (ChangeDelay[x][y] != 0)           /* continue element change */
11371   {
11372     if (change->can_change)
11373     {
11374       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11375
11376       if (IS_ANIMATED(graphic))
11377         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11378
11379       if (change->change_function)
11380         change->change_function(x, y);
11381     }
11382   }
11383   else                                  /* finish element change */
11384   {
11385     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11386     {
11387       page = ChangePage[x][y];
11388       ChangePage[x][y] = -1;
11389
11390       change = &ei->change_page[page];
11391     }
11392
11393     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11394     {
11395       ChangeDelay[x][y] = 1;            /* try change after next move step */
11396       ChangePage[x][y] = page;          /* remember page to use for change */
11397
11398       return;
11399     }
11400
11401 #if 1
11402     /* special case: set new level random seed before changing element */
11403     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11404       handle_action_before_change = TRUE;
11405
11406     if (change->has_action && handle_action_before_change)
11407       ExecuteCustomElementAction(x, y, element, page);
11408 #endif
11409
11410     if (change->can_change)
11411     {
11412       if (ChangeElement(x, y, element, page))
11413       {
11414         if (change->post_change_function)
11415           change->post_change_function(x, y);
11416       }
11417     }
11418
11419     if (change->has_action && !handle_action_before_change)
11420       ExecuteCustomElementAction(x, y, element, page);
11421   }
11422 }
11423
11424 #else
11425
11426 static void HandleElementChange(int x, int y, int page)
11427 {
11428   int element = MovingOrBlocked2Element(x, y);
11429   struct ElementInfo *ei = &element_info[element];
11430   struct ElementChangeInfo *change = &ei->change_page[page];
11431
11432 #ifdef DEBUG
11433   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11434   {
11435     printf("\n\n");
11436     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11437            x, y, element, element_info[element].token_name);
11438     printf("HandleElementChange(): This should never happen!\n");
11439     printf("\n\n");
11440   }
11441 #endif
11442
11443   /* this can happen with classic bombs on walkable, changing elements */
11444   if (!CAN_CHANGE(element))
11445   {
11446 #if 0
11447     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11448       ChangeDelay[x][y] = 0;
11449 #endif
11450
11451     return;
11452   }
11453
11454   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11455   {
11456     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11457
11458     ResetGfxAnimation(x, y);
11459     ResetRandomAnimationValue(x, y);
11460
11461     if (change->pre_change_function)
11462       change->pre_change_function(x, y);
11463   }
11464
11465   ChangeDelay[x][y]--;
11466
11467   if (ChangeDelay[x][y] != 0)           /* continue element change */
11468   {
11469     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11470
11471     if (IS_ANIMATED(graphic))
11472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11473
11474     if (change->change_function)
11475       change->change_function(x, y);
11476   }
11477   else                                  /* finish element change */
11478   {
11479     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11480     {
11481       page = ChangePage[x][y];
11482       ChangePage[x][y] = -1;
11483
11484       change = &ei->change_page[page];
11485     }
11486
11487     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11488     {
11489       ChangeDelay[x][y] = 1;            /* try change after next move step */
11490       ChangePage[x][y] = page;          /* remember page to use for change */
11491
11492       return;
11493     }
11494
11495     if (ChangeElement(x, y, element, page))
11496     {
11497       if (change->post_change_function)
11498         change->post_change_function(x, y);
11499     }
11500   }
11501 }
11502
11503 #endif
11504
11505 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11506                                               int trigger_element,
11507                                               int trigger_event,
11508                                               int trigger_player,
11509                                               int trigger_side,
11510                                               int trigger_page)
11511 {
11512   boolean change_done_any = FALSE;
11513   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11514   int i;
11515
11516   if (!(trigger_events[trigger_element][trigger_event]))
11517     return FALSE;
11518
11519 #if 0
11520   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11521          trigger_event, recursion_loop_depth, recursion_loop_detected,
11522          recursion_loop_element, EL_NAME(recursion_loop_element));
11523 #endif
11524
11525   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11526
11527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11528   {
11529     int element = EL_CUSTOM_START + i;
11530     boolean change_done = FALSE;
11531     int p;
11532
11533     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11534         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11535       continue;
11536
11537     for (p = 0; p < element_info[element].num_change_pages; p++)
11538     {
11539       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11540
11541       if (change->can_change_or_has_action &&
11542           change->has_event[trigger_event] &&
11543           change->trigger_side & trigger_side &&
11544           change->trigger_player & trigger_player &&
11545           change->trigger_page & trigger_page_bits &&
11546           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11547       {
11548         change->actual_trigger_element = trigger_element;
11549         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11550         change->actual_trigger_player_bits = trigger_player;
11551         change->actual_trigger_side = trigger_side;
11552         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11553         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11554
11555 #if 0
11556         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11557                element, EL_NAME(element), p);
11558 #endif
11559
11560         if ((change->can_change && !change_done) || change->has_action)
11561         {
11562           int x, y;
11563
11564           SCAN_PLAYFIELD(x, y)
11565           {
11566             if (Feld[x][y] == element)
11567             {
11568               if (change->can_change && !change_done)
11569               {
11570 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11571                 /* if element already changed in this frame, not only prevent
11572                    another element change (checked in ChangeElement()), but
11573                    also prevent additional element actions for this element */
11574
11575                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11576                     !level.use_action_after_change_bug)
11577                   continue;
11578 #endif
11579
11580 #if 0
11581                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11582                        element, EL_NAME(element), p);
11583 #endif
11584
11585                 ChangeDelay[x][y] = 1;
11586                 ChangeEvent[x][y] = trigger_event;
11587
11588                 HandleElementChange(x, y, p);
11589               }
11590 #if USE_NEW_DELAYED_ACTION
11591               else if (change->has_action)
11592               {
11593 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11594                 /* if element already changed in this frame, not only prevent
11595                    another element change (checked in ChangeElement()), but
11596                    also prevent additional element actions for this element */
11597
11598                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11599                     !level.use_action_after_change_bug)
11600                   continue;
11601 #endif
11602
11603
11604 #if 0
11605                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11606                        element, EL_NAME(element), p);
11607 #endif
11608
11609                 ExecuteCustomElementAction(x, y, element, p);
11610                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11611               }
11612 #else
11613               if (change->has_action)
11614               {
11615                 ExecuteCustomElementAction(x, y, element, p);
11616                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11617               }
11618 #endif
11619             }
11620           }
11621
11622           if (change->can_change)
11623           {
11624             change_done = TRUE;
11625             change_done_any = TRUE;
11626
11627 #if 0
11628             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11629                    element, EL_NAME(element), p);
11630 #endif
11631
11632           }
11633         }
11634       }
11635     }
11636   }
11637
11638   RECURSION_LOOP_DETECTION_END();
11639
11640   return change_done_any;
11641 }
11642
11643 static boolean CheckElementChangeExt(int x, int y,
11644                                      int element,
11645                                      int trigger_element,
11646                                      int trigger_event,
11647                                      int trigger_player,
11648                                      int trigger_side)
11649 {
11650   boolean change_done = FALSE;
11651   int p;
11652
11653   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11654       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11655     return FALSE;
11656
11657   if (Feld[x][y] == EL_BLOCKED)
11658   {
11659     Blocked2Moving(x, y, &x, &y);
11660     element = Feld[x][y];
11661   }
11662
11663 #if 0
11664   /* check if element has already changed */
11665   if (Feld[x][y] != element)
11666     return FALSE;
11667 #else
11668   /* check if element has already changed or is about to change after moving */
11669   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11670        Feld[x][y] != element) ||
11671
11672       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11673        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11674         ChangePage[x][y] != -1)))
11675     return FALSE;
11676 #endif
11677
11678 #if 0
11679   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11680          trigger_event, recursion_loop_depth, recursion_loop_detected,
11681          recursion_loop_element, EL_NAME(recursion_loop_element));
11682 #endif
11683
11684   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11685
11686 #if 0
11687   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11688 #endif
11689
11690   for (p = 0; p < element_info[element].num_change_pages; p++)
11691   {
11692     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11693
11694     /* check trigger element for all events where the element that is checked
11695        for changing interacts with a directly adjacent element -- this is
11696        different to element changes that affect other elements to change on the
11697        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11698     boolean check_trigger_element =
11699       (trigger_event == CE_TOUCHING_X ||
11700        trigger_event == CE_HITTING_X ||
11701        trigger_event == CE_HIT_BY_X ||
11702 #if 1
11703        /* this one was forgotten until 3.2.3 */
11704        trigger_event == CE_DIGGING_X);
11705 #endif
11706
11707     if (change->can_change_or_has_action &&
11708         change->has_event[trigger_event] &&
11709         change->trigger_side & trigger_side &&
11710         change->trigger_player & trigger_player &&
11711         (!check_trigger_element ||
11712          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11713     {
11714       change->actual_trigger_element = trigger_element;
11715       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11716       change->actual_trigger_player_bits = trigger_player;
11717       change->actual_trigger_side = trigger_side;
11718       change->actual_trigger_ce_value = CustomValue[x][y];
11719       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11720
11721       /* special case: trigger element not at (x,y) position for some events */
11722       if (check_trigger_element)
11723       {
11724         static struct
11725         {
11726           int dx, dy;
11727         } move_xy[] =
11728           {
11729             {  0,  0 },
11730             { -1,  0 },
11731             { +1,  0 },
11732             {  0,  0 },
11733             {  0, -1 },
11734             {  0,  0 }, { 0, 0 }, { 0, 0 },
11735             {  0, +1 }
11736           };
11737
11738         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11739         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11740
11741         change->actual_trigger_ce_value = CustomValue[xx][yy];
11742         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11743       }
11744
11745       if (change->can_change && !change_done)
11746       {
11747         ChangeDelay[x][y] = 1;
11748         ChangeEvent[x][y] = trigger_event;
11749
11750         HandleElementChange(x, y, p);
11751
11752         change_done = TRUE;
11753       }
11754 #if USE_NEW_DELAYED_ACTION
11755       else if (change->has_action)
11756       {
11757         ExecuteCustomElementAction(x, y, element, p);
11758         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11759       }
11760 #else
11761       if (change->has_action)
11762       {
11763         ExecuteCustomElementAction(x, y, element, p);
11764         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11765       }
11766 #endif
11767     }
11768   }
11769
11770   RECURSION_LOOP_DETECTION_END();
11771
11772   return change_done;
11773 }
11774
11775 static void PlayPlayerSound(struct PlayerInfo *player)
11776 {
11777   int jx = player->jx, jy = player->jy;
11778   int sound_element = player->artwork_element;
11779   int last_action = player->last_action_waiting;
11780   int action = player->action_waiting;
11781
11782   if (player->is_waiting)
11783   {
11784     if (action != last_action)
11785       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11786     else
11787       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11788   }
11789   else
11790   {
11791     if (action != last_action)
11792       StopSound(element_info[sound_element].sound[last_action]);
11793
11794     if (last_action == ACTION_SLEEPING)
11795       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11796   }
11797 }
11798
11799 static void PlayAllPlayersSound()
11800 {
11801   int i;
11802
11803   for (i = 0; i < MAX_PLAYERS; i++)
11804     if (stored_player[i].active)
11805       PlayPlayerSound(&stored_player[i]);
11806 }
11807
11808 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11809 {
11810   boolean last_waiting = player->is_waiting;
11811   int move_dir = player->MovDir;
11812
11813   player->dir_waiting = move_dir;
11814   player->last_action_waiting = player->action_waiting;
11815
11816   if (is_waiting)
11817   {
11818     if (!last_waiting)          /* not waiting -> waiting */
11819     {
11820       player->is_waiting = TRUE;
11821
11822       player->frame_counter_bored =
11823         FrameCounter +
11824         game.player_boring_delay_fixed +
11825         GetSimpleRandom(game.player_boring_delay_random);
11826       player->frame_counter_sleeping =
11827         FrameCounter +
11828         game.player_sleeping_delay_fixed +
11829         GetSimpleRandom(game.player_sleeping_delay_random);
11830
11831       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11832     }
11833
11834     if (game.player_sleeping_delay_fixed +
11835         game.player_sleeping_delay_random > 0 &&
11836         player->anim_delay_counter == 0 &&
11837         player->post_delay_counter == 0 &&
11838         FrameCounter >= player->frame_counter_sleeping)
11839       player->is_sleeping = TRUE;
11840     else if (game.player_boring_delay_fixed +
11841              game.player_boring_delay_random > 0 &&
11842              FrameCounter >= player->frame_counter_bored)
11843       player->is_bored = TRUE;
11844
11845     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11846                               player->is_bored ? ACTION_BORING :
11847                               ACTION_WAITING);
11848
11849     if (player->is_sleeping && player->use_murphy)
11850     {
11851       /* special case for sleeping Murphy when leaning against non-free tile */
11852
11853       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11854           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11855            !IS_MOVING(player->jx - 1, player->jy)))
11856         move_dir = MV_LEFT;
11857       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11858                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11859                 !IS_MOVING(player->jx + 1, player->jy)))
11860         move_dir = MV_RIGHT;
11861       else
11862         player->is_sleeping = FALSE;
11863
11864       player->dir_waiting = move_dir;
11865     }
11866
11867     if (player->is_sleeping)
11868     {
11869       if (player->num_special_action_sleeping > 0)
11870       {
11871         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11872         {
11873           int last_special_action = player->special_action_sleeping;
11874           int num_special_action = player->num_special_action_sleeping;
11875           int special_action =
11876             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11877              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11878              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11879              last_special_action + 1 : ACTION_SLEEPING);
11880           int special_graphic =
11881             el_act_dir2img(player->artwork_element, special_action, move_dir);
11882
11883           player->anim_delay_counter =
11884             graphic_info[special_graphic].anim_delay_fixed +
11885             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11886           player->post_delay_counter =
11887             graphic_info[special_graphic].post_delay_fixed +
11888             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11889
11890           player->special_action_sleeping = special_action;
11891         }
11892
11893         if (player->anim_delay_counter > 0)
11894         {
11895           player->action_waiting = player->special_action_sleeping;
11896           player->anim_delay_counter--;
11897         }
11898         else if (player->post_delay_counter > 0)
11899         {
11900           player->post_delay_counter--;
11901         }
11902       }
11903     }
11904     else if (player->is_bored)
11905     {
11906       if (player->num_special_action_bored > 0)
11907       {
11908         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11909         {
11910           int special_action =
11911             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11912           int special_graphic =
11913             el_act_dir2img(player->artwork_element, special_action, move_dir);
11914
11915           player->anim_delay_counter =
11916             graphic_info[special_graphic].anim_delay_fixed +
11917             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11918           player->post_delay_counter =
11919             graphic_info[special_graphic].post_delay_fixed +
11920             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11921
11922           player->special_action_bored = special_action;
11923         }
11924
11925         if (player->anim_delay_counter > 0)
11926         {
11927           player->action_waiting = player->special_action_bored;
11928           player->anim_delay_counter--;
11929         }
11930         else if (player->post_delay_counter > 0)
11931         {
11932           player->post_delay_counter--;
11933         }
11934       }
11935     }
11936   }
11937   else if (last_waiting)        /* waiting -> not waiting */
11938   {
11939     player->is_waiting = FALSE;
11940     player->is_bored = FALSE;
11941     player->is_sleeping = FALSE;
11942
11943     player->frame_counter_bored = -1;
11944     player->frame_counter_sleeping = -1;
11945
11946     player->anim_delay_counter = 0;
11947     player->post_delay_counter = 0;
11948
11949     player->dir_waiting = player->MovDir;
11950     player->action_waiting = ACTION_DEFAULT;
11951
11952     player->special_action_bored = ACTION_DEFAULT;
11953     player->special_action_sleeping = ACTION_DEFAULT;
11954   }
11955 }
11956
11957 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11958 {
11959   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11960   int left      = player_action & JOY_LEFT;
11961   int right     = player_action & JOY_RIGHT;
11962   int up        = player_action & JOY_UP;
11963   int down      = player_action & JOY_DOWN;
11964   int button1   = player_action & JOY_BUTTON_1;
11965   int button2   = player_action & JOY_BUTTON_2;
11966   int dx        = (left ? -1 : right ? 1 : 0);
11967   int dy        = (up   ? -1 : down  ? 1 : 0);
11968
11969   if (!player->active || tape.pausing)
11970     return 0;
11971
11972   if (player_action)
11973   {
11974     if (button1)
11975       snapped = SnapField(player, dx, dy);
11976     else
11977     {
11978       if (button2)
11979         dropped = DropElement(player);
11980
11981       moved = MovePlayer(player, dx, dy);
11982     }
11983
11984     if (tape.single_step && tape.recording && !tape.pausing)
11985     {
11986 #if 1
11987       /* as it is called "single step mode", just return to pause mode when the
11988          player stopped moving after one tile (or never starts moving at all) */
11989       if (!player->is_moving)
11990 #else
11991       /* this is buggy: there are quite some cases where the single step mode
11992          does not return to pause mode (like pushing things that don't move
11993          or simply by trying to run against a wall) */
11994       if (button1 || (dropped && !moved))
11995 #endif
11996       {
11997         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11998         SnapField(player, 0, 0);                /* stop snapping */
11999       }
12000     }
12001
12002     SetPlayerWaiting(player, FALSE);
12003
12004     return player_action;
12005   }
12006   else
12007   {
12008     /* no actions for this player (no input at player's configured device) */
12009
12010     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12011     SnapField(player, 0, 0);
12012     CheckGravityMovementWhenNotMoving(player);
12013
12014     if (player->MovPos == 0)
12015       SetPlayerWaiting(player, TRUE);
12016
12017     if (player->MovPos == 0)    /* needed for tape.playing */
12018       player->is_moving = FALSE;
12019
12020     player->is_dropping = FALSE;
12021     player->is_dropping_pressed = FALSE;
12022     player->drop_pressed_delay = 0;
12023
12024     return 0;
12025   }
12026 }
12027
12028 static void CheckLevelTime()
12029 {
12030   int i;
12031
12032   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12033   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12034   {
12035     if (level.native_em_level->lev->home == 0)  /* all players at home */
12036     {
12037       PlayerWins(local_player);
12038
12039       AllPlayersGone = TRUE;
12040
12041       level.native_em_level->lev->home = -1;
12042     }
12043
12044     if (level.native_em_level->ply[0]->alive == 0 &&
12045         level.native_em_level->ply[1]->alive == 0 &&
12046         level.native_em_level->ply[2]->alive == 0 &&
12047         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12048       AllPlayersGone = TRUE;
12049   }
12050   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12051   {
12052     if (game_sp_info.LevelSolved &&
12053         !game_sp_info.GameOver)                         /* game won */
12054     {
12055       PlayerWins(local_player);
12056
12057       game_sp_info.GameOver = TRUE;
12058
12059       AllPlayersGone = TRUE;
12060     }
12061
12062     if (game_sp_info.GameOver)                          /* game lost */
12063       AllPlayersGone = TRUE;
12064   }
12065
12066   if (TimeFrames >= FRAMES_PER_SECOND)
12067   {
12068     TimeFrames = 0;
12069     TapeTime++;
12070
12071     for (i = 0; i < MAX_PLAYERS; i++)
12072     {
12073       struct PlayerInfo *player = &stored_player[i];
12074
12075       if (SHIELD_ON(player))
12076       {
12077         player->shield_normal_time_left--;
12078
12079         if (player->shield_deadly_time_left > 0)
12080           player->shield_deadly_time_left--;
12081       }
12082     }
12083
12084     if (!local_player->LevelSolved && !level.use_step_counter)
12085     {
12086       TimePlayed++;
12087
12088       if (TimeLeft > 0)
12089       {
12090         TimeLeft--;
12091
12092         if (TimeLeft <= 10 && setup.time_limit)
12093           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12094
12095 #if 1
12096         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12097
12098         DisplayGameControlValues();
12099 #else
12100         DrawGameValue_Time(TimeLeft);
12101 #endif
12102
12103         if (!TimeLeft && setup.time_limit)
12104         {
12105           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12106             level.native_em_level->lev->killed_out_of_time = TRUE;
12107           else
12108             for (i = 0; i < MAX_PLAYERS; i++)
12109               KillPlayer(&stored_player[i]);
12110         }
12111       }
12112 #if 1
12113       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12114       {
12115         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12116
12117         DisplayGameControlValues();
12118       }
12119 #else
12120       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12121         DrawGameValue_Time(TimePlayed);
12122 #endif
12123
12124       level.native_em_level->lev->time =
12125         (level.time == 0 ? TimePlayed : TimeLeft);
12126     }
12127
12128     if (tape.recording || tape.playing)
12129       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12130   }
12131
12132 #if 1
12133   UpdateAndDisplayGameControlValues();
12134 #else
12135   UpdateGameDoorValues();
12136   DrawGameDoorValues();
12137 #endif
12138 }
12139
12140 void AdvanceFrameAndPlayerCounters(int player_nr)
12141 {
12142   int i;
12143
12144   /* advance frame counters (global frame counter and time frame counter) */
12145   FrameCounter++;
12146   TimeFrames++;
12147
12148   /* advance player counters (counters for move delay, move animation etc.) */
12149   for (i = 0; i < MAX_PLAYERS; i++)
12150   {
12151     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12152     int move_delay_value = stored_player[i].move_delay_value;
12153     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12154
12155     if (!advance_player_counters)       /* not all players may be affected */
12156       continue;
12157
12158 #if USE_NEW_PLAYER_ANIM
12159     if (move_frames == 0)       /* less than one move per game frame */
12160     {
12161       int stepsize = TILEX / move_delay_value;
12162       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12163       int count = (stored_player[i].is_moving ?
12164                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12165
12166       if (count % delay == 0)
12167         move_frames = 1;
12168     }
12169 #endif
12170
12171     stored_player[i].Frame += move_frames;
12172
12173     if (stored_player[i].MovPos != 0)
12174       stored_player[i].StepFrame += move_frames;
12175
12176     if (stored_player[i].move_delay > 0)
12177       stored_player[i].move_delay--;
12178
12179     /* due to bugs in previous versions, counter must count up, not down */
12180     if (stored_player[i].push_delay != -1)
12181       stored_player[i].push_delay++;
12182
12183     if (stored_player[i].drop_delay > 0)
12184       stored_player[i].drop_delay--;
12185
12186     if (stored_player[i].is_dropping_pressed)
12187       stored_player[i].drop_pressed_delay++;
12188   }
12189 }
12190
12191 void StartGameActions(boolean init_network_game, boolean record_tape,
12192                       long random_seed)
12193 {
12194   unsigned long new_random_seed = InitRND(random_seed);
12195
12196   if (record_tape)
12197     TapeStartRecording(new_random_seed);
12198
12199 #if defined(NETWORK_AVALIABLE)
12200   if (init_network_game)
12201   {
12202     SendToServer_StartPlaying();
12203
12204     return;
12205   }
12206 #endif
12207
12208   InitGame();
12209 }
12210
12211 void GameActions()
12212 {
12213   static unsigned long game_frame_delay = 0;
12214   unsigned long game_frame_delay_value;
12215   byte *recorded_player_action;
12216   byte summarized_player_action = 0;
12217   byte tape_action[MAX_PLAYERS];
12218   int i;
12219
12220   /* detect endless loops, caused by custom element programming */
12221   if (recursion_loop_detected && recursion_loop_depth == 0)
12222   {
12223     char *message = getStringCat3("Internal Error ! Element ",
12224                                   EL_NAME(recursion_loop_element),
12225                                   " caused endless loop ! Quit the game ?");
12226
12227     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12228           EL_NAME(recursion_loop_element));
12229
12230     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12231
12232     recursion_loop_detected = FALSE;    /* if game should be continued */
12233
12234     free(message);
12235
12236     return;
12237   }
12238
12239   if (game.restart_level)
12240     StartGameActions(options.network, setup.autorecord, level.random_seed);
12241
12242   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12243   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12244   {
12245     if (level.native_em_level->lev->home == 0)  /* all players at home */
12246     {
12247       PlayerWins(local_player);
12248
12249       AllPlayersGone = TRUE;
12250
12251       level.native_em_level->lev->home = -1;
12252     }
12253
12254     if (level.native_em_level->ply[0]->alive == 0 &&
12255         level.native_em_level->ply[1]->alive == 0 &&
12256         level.native_em_level->ply[2]->alive == 0 &&
12257         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12258       AllPlayersGone = TRUE;
12259   }
12260   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12261   {
12262     if (game_sp_info.LevelSolved &&
12263         !game_sp_info.GameOver)                         /* game won */
12264     {
12265       PlayerWins(local_player);
12266
12267       game_sp_info.GameOver = TRUE;
12268
12269       AllPlayersGone = TRUE;
12270     }
12271
12272     if (game_sp_info.GameOver)                          /* game lost */
12273       AllPlayersGone = TRUE;
12274   }
12275
12276   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12277     GameWon();
12278
12279   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12280     TapeStop();
12281
12282   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12283     return;
12284
12285   game_frame_delay_value =
12286     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12287
12288   if (tape.playing && tape.warp_forward && !tape.pausing)
12289     game_frame_delay_value = 0;
12290
12291   /* ---------- main game synchronization point ---------- */
12292
12293   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12294
12295   if (network_playing && !network_player_action_received)
12296   {
12297     /* try to get network player actions in time */
12298
12299 #if defined(NETWORK_AVALIABLE)
12300     /* last chance to get network player actions without main loop delay */
12301     HandleNetworking();
12302 #endif
12303
12304     /* game was quit by network peer */
12305     if (game_status != GAME_MODE_PLAYING)
12306       return;
12307
12308     if (!network_player_action_received)
12309       return;           /* failed to get network player actions in time */
12310
12311     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12312   }
12313
12314   if (tape.pausing)
12315     return;
12316
12317   /* at this point we know that we really continue executing the game */
12318
12319   network_player_action_received = FALSE;
12320
12321   /* when playing tape, read previously recorded player input from tape data */
12322   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12323
12324 #if 1
12325   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12326   if (tape.pausing)
12327     return;
12328 #endif
12329
12330   if (tape.set_centered_player)
12331   {
12332     game.centered_player_nr_next = tape.centered_player_nr_next;
12333     game.set_centered_player = TRUE;
12334   }
12335
12336   for (i = 0; i < MAX_PLAYERS; i++)
12337   {
12338     summarized_player_action |= stored_player[i].action;
12339
12340     if (!network_playing)
12341       stored_player[i].effective_action = stored_player[i].action;
12342   }
12343
12344 #if defined(NETWORK_AVALIABLE)
12345   if (network_playing)
12346     SendToServer_MovePlayer(summarized_player_action);
12347 #endif
12348
12349   if (!options.network && !setup.team_mode)
12350     local_player->effective_action = summarized_player_action;
12351
12352   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12353   {
12354     for (i = 0; i < MAX_PLAYERS; i++)
12355       stored_player[i].effective_action =
12356         (i == game.centered_player_nr ? summarized_player_action : 0);
12357   }
12358
12359   if (recorded_player_action != NULL)
12360     for (i = 0; i < MAX_PLAYERS; i++)
12361       stored_player[i].effective_action = recorded_player_action[i];
12362
12363   for (i = 0; i < MAX_PLAYERS; i++)
12364   {
12365     tape_action[i] = stored_player[i].effective_action;
12366
12367     /* (this can only happen in the R'n'D game engine) */
12368     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12369       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12370   }
12371
12372   /* only record actions from input devices, but not programmed actions */
12373   if (tape.recording)
12374     TapeRecordAction(tape_action);
12375
12376 #if USE_NEW_PLAYER_ASSIGNMENTS
12377   {
12378     byte mapped_action[MAX_PLAYERS];
12379
12380     for (i = 0; i < MAX_PLAYERS; i++)
12381       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12382
12383     for (i = 0; i < MAX_PLAYERS; i++)
12384       stored_player[i].effective_action = mapped_action[i];
12385   }
12386 #endif
12387
12388   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12389   {
12390     GameActions_EM_Main();
12391   }
12392   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12393   {
12394     GameActions_SP_Main();
12395   }
12396   else
12397   {
12398     GameActions_RND();
12399   }
12400 }
12401
12402 void GameActions_EM_Main()
12403 {
12404   byte effective_action[MAX_PLAYERS];
12405   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12406   int i;
12407
12408   for (i = 0; i < MAX_PLAYERS; i++)
12409     effective_action[i] = stored_player[i].effective_action;
12410
12411   GameActions_EM(effective_action, warp_mode);
12412
12413   CheckLevelTime();
12414
12415   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12416 }
12417
12418 void GameActions_SP_Main()
12419 {
12420   byte effective_action[MAX_PLAYERS];
12421   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12422   int i;
12423
12424   for (i = 0; i < MAX_PLAYERS; i++)
12425     effective_action[i] = stored_player[i].effective_action;
12426
12427   GameActions_SP(effective_action, warp_mode);
12428
12429   CheckLevelTime();
12430
12431   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12432 }
12433
12434 void GameActions_RND()
12435 {
12436   int magic_wall_x = 0, magic_wall_y = 0;
12437   int i, x, y, element, graphic;
12438
12439   InitPlayfieldScanModeVars();
12440
12441 #if USE_ONE_MORE_CHANGE_PER_FRAME
12442   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12443   {
12444     SCAN_PLAYFIELD(x, y)
12445     {
12446       ChangeCount[x][y] = 0;
12447       ChangeEvent[x][y] = -1;
12448     }
12449   }
12450 #endif
12451
12452   if (game.set_centered_player)
12453   {
12454     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12455
12456     /* switching to "all players" only possible if all players fit to screen */
12457     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12458     {
12459       game.centered_player_nr_next = game.centered_player_nr;
12460       game.set_centered_player = FALSE;
12461     }
12462
12463     /* do not switch focus to non-existing (or non-active) player */
12464     if (game.centered_player_nr_next >= 0 &&
12465         !stored_player[game.centered_player_nr_next].active)
12466     {
12467       game.centered_player_nr_next = game.centered_player_nr;
12468       game.set_centered_player = FALSE;
12469     }
12470   }
12471
12472   if (game.set_centered_player &&
12473       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12474   {
12475     int sx, sy;
12476
12477     if (game.centered_player_nr_next == -1)
12478     {
12479       setScreenCenteredToAllPlayers(&sx, &sy);
12480     }
12481     else
12482     {
12483       sx = stored_player[game.centered_player_nr_next].jx;
12484       sy = stored_player[game.centered_player_nr_next].jy;
12485     }
12486
12487     game.centered_player_nr = game.centered_player_nr_next;
12488     game.set_centered_player = FALSE;
12489
12490     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12491     DrawGameDoorValues();
12492   }
12493
12494   for (i = 0; i < MAX_PLAYERS; i++)
12495   {
12496     int actual_player_action = stored_player[i].effective_action;
12497
12498 #if 1
12499     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12500        - rnd_equinox_tetrachloride 048
12501        - rnd_equinox_tetrachloride_ii 096
12502        - rnd_emanuel_schmieg 002
12503        - doctor_sloan_ww 001, 020
12504     */
12505     if (stored_player[i].MovPos == 0)
12506       CheckGravityMovement(&stored_player[i]);
12507 #endif
12508
12509     /* overwrite programmed action with tape action */
12510     if (stored_player[i].programmed_action)
12511       actual_player_action = stored_player[i].programmed_action;
12512
12513     PlayerActions(&stored_player[i], actual_player_action);
12514
12515     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12516   }
12517
12518   ScrollScreen(NULL, SCROLL_GO_ON);
12519
12520   /* for backwards compatibility, the following code emulates a fixed bug that
12521      occured when pushing elements (causing elements that just made their last
12522      pushing step to already (if possible) make their first falling step in the
12523      same game frame, which is bad); this code is also needed to use the famous
12524      "spring push bug" which is used in older levels and might be wanted to be
12525      used also in newer levels, but in this case the buggy pushing code is only
12526      affecting the "spring" element and no other elements */
12527
12528   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12529   {
12530     for (i = 0; i < MAX_PLAYERS; i++)
12531     {
12532       struct PlayerInfo *player = &stored_player[i];
12533       int x = player->jx;
12534       int y = player->jy;
12535
12536       if (player->active && player->is_pushing && player->is_moving &&
12537           IS_MOVING(x, y) &&
12538           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12539            Feld[x][y] == EL_SPRING))
12540       {
12541         ContinueMoving(x, y);
12542
12543         /* continue moving after pushing (this is actually a bug) */
12544         if (!IS_MOVING(x, y))
12545           Stop[x][y] = FALSE;
12546       }
12547     }
12548   }
12549
12550 #if 0
12551   debug_print_timestamp(0, "start main loop profiling");
12552 #endif
12553
12554   SCAN_PLAYFIELD(x, y)
12555   {
12556     ChangeCount[x][y] = 0;
12557     ChangeEvent[x][y] = -1;
12558
12559     /* this must be handled before main playfield loop */
12560     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12561     {
12562       MovDelay[x][y]--;
12563       if (MovDelay[x][y] <= 0)
12564         RemoveField(x, y);
12565     }
12566
12567 #if USE_NEW_SNAP_DELAY
12568     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12569     {
12570       MovDelay[x][y]--;
12571       if (MovDelay[x][y] <= 0)
12572       {
12573         RemoveField(x, y);
12574         TEST_DrawLevelField(x, y);
12575
12576         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12577       }
12578     }
12579 #endif
12580
12581 #if DEBUG
12582     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12583     {
12584       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12585       printf("GameActions(): This should never happen!\n");
12586
12587       ChangePage[x][y] = -1;
12588     }
12589 #endif
12590
12591     Stop[x][y] = FALSE;
12592     if (WasJustMoving[x][y] > 0)
12593       WasJustMoving[x][y]--;
12594     if (WasJustFalling[x][y] > 0)
12595       WasJustFalling[x][y]--;
12596     if (CheckCollision[x][y] > 0)
12597       CheckCollision[x][y]--;
12598     if (CheckImpact[x][y] > 0)
12599       CheckImpact[x][y]--;
12600
12601     GfxFrame[x][y]++;
12602
12603     /* reset finished pushing action (not done in ContinueMoving() to allow
12604        continuous pushing animation for elements with zero push delay) */
12605     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12606     {
12607       ResetGfxAnimation(x, y);
12608       TEST_DrawLevelField(x, y);
12609     }
12610
12611 #if DEBUG
12612     if (IS_BLOCKED(x, y))
12613     {
12614       int oldx, oldy;
12615
12616       Blocked2Moving(x, y, &oldx, &oldy);
12617       if (!IS_MOVING(oldx, oldy))
12618       {
12619         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12620         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12621         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12622         printf("GameActions(): This should never happen!\n");
12623       }
12624     }
12625 #endif
12626   }
12627
12628 #if 0
12629   debug_print_timestamp(0, "- time for pre-main loop:");
12630 #endif
12631
12632 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12633   SCAN_PLAYFIELD(x, y)
12634   {
12635     element = Feld[x][y];
12636     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12637
12638 #if 1
12639     {
12640 #if 1
12641       int element2 = element;
12642       int graphic2 = graphic;
12643 #else
12644       int element2 = Feld[x][y];
12645       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12646 #endif
12647       int last_gfx_frame = GfxFrame[x][y];
12648
12649       if (graphic_info[graphic2].anim_global_sync)
12650         GfxFrame[x][y] = FrameCounter;
12651       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12652         GfxFrame[x][y] = CustomValue[x][y];
12653       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12654         GfxFrame[x][y] = element_info[element2].collect_score;
12655       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12656         GfxFrame[x][y] = ChangeDelay[x][y];
12657
12658       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12659         DrawLevelGraphicAnimation(x, y, graphic2);
12660     }
12661 #else
12662     ResetGfxFrame(x, y, TRUE);
12663 #endif
12664
12665 #if 1
12666     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12667         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12668       ResetRandomAnimationValue(x, y);
12669 #endif
12670
12671 #if 1
12672     SetRandomAnimationValue(x, y);
12673 #endif
12674
12675 #if 1
12676     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12677 #endif
12678   }
12679 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12680
12681 #if 0
12682   debug_print_timestamp(0, "- time for TEST loop:     -->");
12683 #endif
12684
12685   SCAN_PLAYFIELD(x, y)
12686   {
12687     element = Feld[x][y];
12688     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12689
12690     ResetGfxFrame(x, y, TRUE);
12691
12692     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12693         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12694       ResetRandomAnimationValue(x, y);
12695
12696     SetRandomAnimationValue(x, y);
12697
12698     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12699
12700     if (IS_INACTIVE(element))
12701     {
12702       if (IS_ANIMATED(graphic))
12703         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12704
12705       continue;
12706     }
12707
12708     /* this may take place after moving, so 'element' may have changed */
12709     if (IS_CHANGING(x, y) &&
12710         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12711     {
12712       int page = element_info[element].event_page_nr[CE_DELAY];
12713
12714 #if 1
12715       HandleElementChange(x, y, page);
12716 #else
12717       if (CAN_CHANGE(element))
12718         HandleElementChange(x, y, page);
12719
12720       if (HAS_ACTION(element))
12721         ExecuteCustomElementAction(x, y, element, page);
12722 #endif
12723
12724       element = Feld[x][y];
12725       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12726     }
12727
12728 #if 0   // ---------------------------------------------------------------------
12729
12730     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12731     {
12732       StartMoving(x, y);
12733
12734       element = Feld[x][y];
12735       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12736
12737       if (IS_ANIMATED(graphic) &&
12738           !IS_MOVING(x, y) &&
12739           !Stop[x][y])
12740         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12741
12742       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12743         TEST_DrawTwinkleOnField(x, y);
12744     }
12745     else if (IS_MOVING(x, y))
12746       ContinueMoving(x, y);
12747     else
12748     {
12749       switch (element)
12750       {
12751         case EL_ACID:
12752         case EL_EXIT_OPEN:
12753         case EL_EM_EXIT_OPEN:
12754         case EL_SP_EXIT_OPEN:
12755         case EL_STEEL_EXIT_OPEN:
12756         case EL_EM_STEEL_EXIT_OPEN:
12757         case EL_SP_TERMINAL:
12758         case EL_SP_TERMINAL_ACTIVE:
12759         case EL_EXTRA_TIME:
12760         case EL_SHIELD_NORMAL:
12761         case EL_SHIELD_DEADLY:
12762           if (IS_ANIMATED(graphic))
12763             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12764           break;
12765
12766         case EL_DYNAMITE_ACTIVE:
12767         case EL_EM_DYNAMITE_ACTIVE:
12768         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12769         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12770         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12771         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12772         case EL_SP_DISK_RED_ACTIVE:
12773           CheckDynamite(x, y);
12774           break;
12775
12776         case EL_AMOEBA_GROWING:
12777           AmoebeWaechst(x, y);
12778           break;
12779
12780         case EL_AMOEBA_SHRINKING:
12781           AmoebaDisappearing(x, y);
12782           break;
12783
12784 #if !USE_NEW_AMOEBA_CODE
12785         case EL_AMOEBA_WET:
12786         case EL_AMOEBA_DRY:
12787         case EL_AMOEBA_FULL:
12788         case EL_BD_AMOEBA:
12789         case EL_EMC_DRIPPER:
12790           AmoebeAbleger(x, y);
12791           break;
12792 #endif
12793
12794         case EL_GAME_OF_LIFE:
12795         case EL_BIOMAZE:
12796           Life(x, y);
12797           break;
12798
12799         case EL_EXIT_CLOSED:
12800           CheckExit(x, y);
12801           break;
12802
12803         case EL_EM_EXIT_CLOSED:
12804           CheckExitEM(x, y);
12805           break;
12806
12807         case EL_STEEL_EXIT_CLOSED:
12808           CheckExitSteel(x, y);
12809           break;
12810
12811         case EL_EM_STEEL_EXIT_CLOSED:
12812           CheckExitSteelEM(x, y);
12813           break;
12814
12815         case EL_SP_EXIT_CLOSED:
12816           CheckExitSP(x, y);
12817           break;
12818
12819         case EL_EXPANDABLE_WALL_GROWING:
12820         case EL_EXPANDABLE_STEELWALL_GROWING:
12821           MauerWaechst(x, y);
12822           break;
12823
12824         case EL_EXPANDABLE_WALL:
12825         case EL_EXPANDABLE_WALL_HORIZONTAL:
12826         case EL_EXPANDABLE_WALL_VERTICAL:
12827         case EL_EXPANDABLE_WALL_ANY:
12828         case EL_BD_EXPANDABLE_WALL:
12829           MauerAbleger(x, y);
12830           break;
12831
12832         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12833         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12834         case EL_EXPANDABLE_STEELWALL_ANY:
12835           MauerAblegerStahl(x, y);
12836           break;
12837
12838         case EL_FLAMES:
12839           CheckForDragon(x, y);
12840           break;
12841
12842         case EL_EXPLOSION:
12843           break;
12844
12845         case EL_ELEMENT_SNAPPING:
12846         case EL_DIAGONAL_SHRINKING:
12847         case EL_DIAGONAL_GROWING:
12848         {
12849           graphic =
12850             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12851
12852           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12853           break;
12854         }
12855
12856         default:
12857           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12858             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12859           break;
12860       }
12861     }
12862
12863 #else   // ---------------------------------------------------------------------
12864
12865     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12866     {
12867       StartMoving(x, y);
12868
12869       element = Feld[x][y];
12870       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12871
12872       if (IS_ANIMATED(graphic) &&
12873           !IS_MOVING(x, y) &&
12874           !Stop[x][y])
12875         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12876
12877       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12878         TEST_DrawTwinkleOnField(x, y);
12879     }
12880     else if ((element == EL_ACID ||
12881               element == EL_EXIT_OPEN ||
12882               element == EL_EM_EXIT_OPEN ||
12883               element == EL_SP_EXIT_OPEN ||
12884               element == EL_STEEL_EXIT_OPEN ||
12885               element == EL_EM_STEEL_EXIT_OPEN ||
12886               element == EL_SP_TERMINAL ||
12887               element == EL_SP_TERMINAL_ACTIVE ||
12888               element == EL_EXTRA_TIME ||
12889               element == EL_SHIELD_NORMAL ||
12890               element == EL_SHIELD_DEADLY) &&
12891              IS_ANIMATED(graphic))
12892       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12893     else if (IS_MOVING(x, y))
12894       ContinueMoving(x, y);
12895     else if (IS_ACTIVE_BOMB(element))
12896       CheckDynamite(x, y);
12897     else if (element == EL_AMOEBA_GROWING)
12898       AmoebeWaechst(x, y);
12899     else if (element == EL_AMOEBA_SHRINKING)
12900       AmoebaDisappearing(x, y);
12901
12902 #if !USE_NEW_AMOEBA_CODE
12903     else if (IS_AMOEBALIVE(element))
12904       AmoebeAbleger(x, y);
12905 #endif
12906
12907     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12908       Life(x, y);
12909     else if (element == EL_EXIT_CLOSED)
12910       CheckExit(x, y);
12911     else if (element == EL_EM_EXIT_CLOSED)
12912       CheckExitEM(x, y);
12913     else if (element == EL_STEEL_EXIT_CLOSED)
12914       CheckExitSteel(x, y);
12915     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12916       CheckExitSteelEM(x, y);
12917     else if (element == EL_SP_EXIT_CLOSED)
12918       CheckExitSP(x, y);
12919     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12920              element == EL_EXPANDABLE_STEELWALL_GROWING)
12921       MauerWaechst(x, y);
12922     else if (element == EL_EXPANDABLE_WALL ||
12923              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12924              element == EL_EXPANDABLE_WALL_VERTICAL ||
12925              element == EL_EXPANDABLE_WALL_ANY ||
12926              element == EL_BD_EXPANDABLE_WALL)
12927       MauerAbleger(x, y);
12928     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12929              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12930              element == EL_EXPANDABLE_STEELWALL_ANY)
12931       MauerAblegerStahl(x, y);
12932     else if (element == EL_FLAMES)
12933       CheckForDragon(x, y);
12934     else if (element == EL_EXPLOSION)
12935       ; /* drawing of correct explosion animation is handled separately */
12936     else if (element == EL_ELEMENT_SNAPPING ||
12937              element == EL_DIAGONAL_SHRINKING ||
12938              element == EL_DIAGONAL_GROWING)
12939     {
12940       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12941
12942       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12943     }
12944     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12946
12947 #endif  // ---------------------------------------------------------------------
12948
12949     if (IS_BELT_ACTIVE(element))
12950       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12951
12952     if (game.magic_wall_active)
12953     {
12954       int jx = local_player->jx, jy = local_player->jy;
12955
12956       /* play the element sound at the position nearest to the player */
12957       if ((element == EL_MAGIC_WALL_FULL ||
12958            element == EL_MAGIC_WALL_ACTIVE ||
12959            element == EL_MAGIC_WALL_EMPTYING ||
12960            element == EL_BD_MAGIC_WALL_FULL ||
12961            element == EL_BD_MAGIC_WALL_ACTIVE ||
12962            element == EL_BD_MAGIC_WALL_EMPTYING ||
12963            element == EL_DC_MAGIC_WALL_FULL ||
12964            element == EL_DC_MAGIC_WALL_ACTIVE ||
12965            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12966           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12967       {
12968         magic_wall_x = x;
12969         magic_wall_y = y;
12970       }
12971     }
12972   }
12973
12974 #if 0
12975   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12976 #endif
12977
12978 #if USE_NEW_AMOEBA_CODE
12979   /* new experimental amoeba growth stuff */
12980   if (!(FrameCounter % 8))
12981   {
12982     static unsigned long random = 1684108901;
12983
12984     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12985     {
12986       x = RND(lev_fieldx);
12987       y = RND(lev_fieldy);
12988       element = Feld[x][y];
12989
12990       if (!IS_PLAYER(x,y) &&
12991           (element == EL_EMPTY ||
12992            CAN_GROW_INTO(element) ||
12993            element == EL_QUICKSAND_EMPTY ||
12994            element == EL_QUICKSAND_FAST_EMPTY ||
12995            element == EL_ACID_SPLASH_LEFT ||
12996            element == EL_ACID_SPLASH_RIGHT))
12997       {
12998         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12999             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13000             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13001             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13002           Feld[x][y] = EL_AMOEBA_DROP;
13003       }
13004
13005       random = random * 129 + 1;
13006     }
13007   }
13008 #endif
13009
13010 #if 0
13011   if (game.explosions_delayed)
13012 #endif
13013   {
13014     game.explosions_delayed = FALSE;
13015
13016     SCAN_PLAYFIELD(x, y)
13017     {
13018       element = Feld[x][y];
13019
13020       if (ExplodeField[x][y])
13021         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13022       else if (element == EL_EXPLOSION)
13023         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13024
13025       ExplodeField[x][y] = EX_TYPE_NONE;
13026     }
13027
13028     game.explosions_delayed = TRUE;
13029   }
13030
13031   if (game.magic_wall_active)
13032   {
13033     if (!(game.magic_wall_time_left % 4))
13034     {
13035       int element = Feld[magic_wall_x][magic_wall_y];
13036
13037       if (element == EL_BD_MAGIC_WALL_FULL ||
13038           element == EL_BD_MAGIC_WALL_ACTIVE ||
13039           element == EL_BD_MAGIC_WALL_EMPTYING)
13040         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13041       else if (element == EL_DC_MAGIC_WALL_FULL ||
13042                element == EL_DC_MAGIC_WALL_ACTIVE ||
13043                element == EL_DC_MAGIC_WALL_EMPTYING)
13044         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13045       else
13046         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13047     }
13048
13049     if (game.magic_wall_time_left > 0)
13050     {
13051       game.magic_wall_time_left--;
13052
13053       if (!game.magic_wall_time_left)
13054       {
13055         SCAN_PLAYFIELD(x, y)
13056         {
13057           element = Feld[x][y];
13058
13059           if (element == EL_MAGIC_WALL_ACTIVE ||
13060               element == EL_MAGIC_WALL_FULL)
13061           {
13062             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13063             TEST_DrawLevelField(x, y);
13064           }
13065           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13066                    element == EL_BD_MAGIC_WALL_FULL)
13067           {
13068             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13069             TEST_DrawLevelField(x, y);
13070           }
13071           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13072                    element == EL_DC_MAGIC_WALL_FULL)
13073           {
13074             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13075             TEST_DrawLevelField(x, y);
13076           }
13077         }
13078
13079         game.magic_wall_active = FALSE;
13080       }
13081     }
13082   }
13083
13084   if (game.light_time_left > 0)
13085   {
13086     game.light_time_left--;
13087
13088     if (game.light_time_left == 0)
13089       RedrawAllLightSwitchesAndInvisibleElements();
13090   }
13091
13092   if (game.timegate_time_left > 0)
13093   {
13094     game.timegate_time_left--;
13095
13096     if (game.timegate_time_left == 0)
13097       CloseAllOpenTimegates();
13098   }
13099
13100   if (game.lenses_time_left > 0)
13101   {
13102     game.lenses_time_left--;
13103
13104     if (game.lenses_time_left == 0)
13105       RedrawAllInvisibleElementsForLenses();
13106   }
13107
13108   if (game.magnify_time_left > 0)
13109   {
13110     game.magnify_time_left--;
13111
13112     if (game.magnify_time_left == 0)
13113       RedrawAllInvisibleElementsForMagnifier();
13114   }
13115
13116   for (i = 0; i < MAX_PLAYERS; i++)
13117   {
13118     struct PlayerInfo *player = &stored_player[i];
13119
13120     if (SHIELD_ON(player))
13121     {
13122       if (player->shield_deadly_time_left)
13123         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13124       else if (player->shield_normal_time_left)
13125         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13126     }
13127   }
13128
13129 #if USE_DELAYED_GFX_REDRAW
13130   SCAN_PLAYFIELD(x, y)
13131   {
13132 #if 1
13133     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13134 #else
13135     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13136         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13137 #endif
13138     {
13139       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13140          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13141
13142       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13143         DrawLevelField(x, y);
13144
13145       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13146         DrawLevelFieldCrumbledSand(x, y);
13147
13148       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13149         DrawLevelFieldCrumbledSandNeighbours(x, y);
13150
13151       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13152         DrawTwinkleOnField(x, y);
13153     }
13154
13155     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13156   }
13157 #endif
13158
13159   CheckLevelTime();
13160
13161   DrawAllPlayers();
13162   PlayAllPlayersSound();
13163
13164   if (options.debug)                    /* calculate frames per second */
13165   {
13166     static unsigned long fps_counter = 0;
13167     static int fps_frames = 0;
13168     unsigned long fps_delay_ms = Counter() - fps_counter;
13169
13170     fps_frames++;
13171
13172     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13173     {
13174       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13175
13176       fps_frames = 0;
13177       fps_counter = Counter();
13178     }
13179
13180     redraw_mask |= REDRAW_FPS;
13181   }
13182
13183   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13184
13185   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13186   {
13187     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13188
13189     local_player->show_envelope = 0;
13190   }
13191
13192 #if 0
13193   debug_print_timestamp(0, "stop main loop profiling ");
13194   printf("----------------------------------------------------------\n");
13195 #endif
13196
13197   /* use random number generator in every frame to make it less predictable */
13198   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13199     RND(1);
13200 }
13201
13202 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13203 {
13204   int min_x = x, min_y = y, max_x = x, max_y = y;
13205   int i;
13206
13207   for (i = 0; i < MAX_PLAYERS; i++)
13208   {
13209     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13210
13211     if (!stored_player[i].active || &stored_player[i] == player)
13212       continue;
13213
13214     min_x = MIN(min_x, jx);
13215     min_y = MIN(min_y, jy);
13216     max_x = MAX(max_x, jx);
13217     max_y = MAX(max_y, jy);
13218   }
13219
13220   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13221 }
13222
13223 static boolean AllPlayersInVisibleScreen()
13224 {
13225   int i;
13226
13227   for (i = 0; i < MAX_PLAYERS; i++)
13228   {
13229     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13230
13231     if (!stored_player[i].active)
13232       continue;
13233
13234     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13235       return FALSE;
13236   }
13237
13238   return TRUE;
13239 }
13240
13241 void ScrollLevel(int dx, int dy)
13242 {
13243 #if 0
13244   /* (directly solved in BlitBitmap() now) */
13245   static Bitmap *bitmap_db_field2 = NULL;
13246   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13247   int x, y;
13248 #else
13249   int x, y;
13250 #endif
13251
13252 #if 0
13253   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13254   /* only horizontal XOR vertical scroll direction allowed */
13255   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13256     return;
13257 #endif
13258
13259 #if 0
13260   /* (directly solved in BlitBitmap() now) */
13261   if (bitmap_db_field2 == NULL)
13262     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13263
13264   /* needed when blitting directly to same bitmap -- should not be needed with
13265      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13266   BlitBitmap(drawto_field, bitmap_db_field2,
13267              FX + TILEX * (dx == -1) - softscroll_offset,
13268              FY + TILEY * (dy == -1) - softscroll_offset,
13269              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13270              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13271              FX + TILEX * (dx == 1) - softscroll_offset,
13272              FY + TILEY * (dy == 1) - softscroll_offset);
13273   BlitBitmap(bitmap_db_field2, drawto_field,
13274              FX + TILEX * (dx == 1) - softscroll_offset,
13275              FY + TILEY * (dy == 1) - softscroll_offset,
13276              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13277              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13278              FX + TILEX * (dx == 1) - softscroll_offset,
13279              FY + TILEY * (dy == 1) - softscroll_offset);
13280
13281 #else
13282
13283 #if 0
13284   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13285   int xsize = (BX2 - BX1 + 1);
13286   int ysize = (BY2 - BY1 + 1);
13287   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13288   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13289   int step  = (start < end ? +1 : -1);
13290
13291   for (i = start; i != end; i += step)
13292   {
13293     BlitBitmap(drawto_field, drawto_field,
13294                FX + TILEX * (dx != 0 ? i + step : 0),
13295                FY + TILEY * (dy != 0 ? i + step : 0),
13296                TILEX * (dx != 0 ? 1 : xsize),
13297                TILEY * (dy != 0 ? 1 : ysize),
13298                FX + TILEX * (dx != 0 ? i : 0),
13299                FY + TILEY * (dy != 0 ? i : 0));
13300   }
13301
13302 #else
13303
13304   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13305
13306   BlitBitmap(drawto_field, drawto_field,
13307              FX + TILEX * (dx == -1) - softscroll_offset,
13308              FY + TILEY * (dy == -1) - softscroll_offset,
13309              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13310              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13311              FX + TILEX * (dx == 1) - softscroll_offset,
13312              FY + TILEY * (dy == 1) - softscroll_offset);
13313 #endif
13314 #endif
13315
13316   if (dx != 0)
13317   {
13318     x = (dx == 1 ? BX1 : BX2);
13319     for (y = BY1; y <= BY2; y++)
13320       DrawScreenField(x, y);
13321   }
13322
13323   if (dy != 0)
13324   {
13325     y = (dy == 1 ? BY1 : BY2);
13326     for (x = BX1; x <= BX2; x++)
13327       DrawScreenField(x, y);
13328   }
13329
13330   redraw_mask |= REDRAW_FIELD;
13331 }
13332
13333 static boolean canFallDown(struct PlayerInfo *player)
13334 {
13335   int jx = player->jx, jy = player->jy;
13336
13337   return (IN_LEV_FIELD(jx, jy + 1) &&
13338           (IS_FREE(jx, jy + 1) ||
13339            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13340           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13341           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13342 }
13343
13344 static boolean canPassField(int x, int y, int move_dir)
13345 {
13346   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13347   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13348   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13349   int nextx = x + dx;
13350   int nexty = y + dy;
13351   int element = Feld[x][y];
13352
13353   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13354           !CAN_MOVE(element) &&
13355           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13356           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13357           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13358 }
13359
13360 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13361 {
13362   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13363   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13364   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13365   int newx = x + dx;
13366   int newy = y + dy;
13367
13368   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13369           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13370           (IS_DIGGABLE(Feld[newx][newy]) ||
13371            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13372            canPassField(newx, newy, move_dir)));
13373 }
13374
13375 static void CheckGravityMovement(struct PlayerInfo *player)
13376 {
13377 #if USE_PLAYER_GRAVITY
13378   if (player->gravity && !player->programmed_action)
13379 #else
13380   if (game.gravity && !player->programmed_action)
13381 #endif
13382   {
13383     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13384     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13385     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13386     int jx = player->jx, jy = player->jy;
13387     boolean player_is_moving_to_valid_field =
13388       (!player_is_snapping &&
13389        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13390         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13391     boolean player_can_fall_down = canFallDown(player);
13392
13393     if (player_can_fall_down &&
13394         !player_is_moving_to_valid_field)
13395       player->programmed_action = MV_DOWN;
13396   }
13397 }
13398
13399 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13400 {
13401   return CheckGravityMovement(player);
13402
13403 #if USE_PLAYER_GRAVITY
13404   if (player->gravity && !player->programmed_action)
13405 #else
13406   if (game.gravity && !player->programmed_action)
13407 #endif
13408   {
13409     int jx = player->jx, jy = player->jy;
13410     boolean field_under_player_is_free =
13411       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13412     boolean player_is_standing_on_valid_field =
13413       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13414        (IS_WALKABLE(Feld[jx][jy]) &&
13415         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13416
13417     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13418       player->programmed_action = MV_DOWN;
13419   }
13420 }
13421
13422 /*
13423   MovePlayerOneStep()
13424   -----------------------------------------------------------------------------
13425   dx, dy:               direction (non-diagonal) to try to move the player to
13426   real_dx, real_dy:     direction as read from input device (can be diagonal)
13427 */
13428
13429 boolean MovePlayerOneStep(struct PlayerInfo *player,
13430                           int dx, int dy, int real_dx, int real_dy)
13431 {
13432   int jx = player->jx, jy = player->jy;
13433   int new_jx = jx + dx, new_jy = jy + dy;
13434 #if !USE_FIXED_DONT_RUN_INTO
13435   int element;
13436 #endif
13437   int can_move;
13438   boolean player_can_move = !player->cannot_move;
13439
13440   if (!player->active || (!dx && !dy))
13441     return MP_NO_ACTION;
13442
13443   player->MovDir = (dx < 0 ? MV_LEFT :
13444                     dx > 0 ? MV_RIGHT :
13445                     dy < 0 ? MV_UP :
13446                     dy > 0 ? MV_DOWN :  MV_NONE);
13447
13448   if (!IN_LEV_FIELD(new_jx, new_jy))
13449     return MP_NO_ACTION;
13450
13451   if (!player_can_move)
13452   {
13453     if (player->MovPos == 0)
13454     {
13455       player->is_moving = FALSE;
13456       player->is_digging = FALSE;
13457       player->is_collecting = FALSE;
13458       player->is_snapping = FALSE;
13459       player->is_pushing = FALSE;
13460     }
13461   }
13462
13463 #if 1
13464   if (!options.network && game.centered_player_nr == -1 &&
13465       !AllPlayersInSight(player, new_jx, new_jy))
13466     return MP_NO_ACTION;
13467 #else
13468   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13469     return MP_NO_ACTION;
13470 #endif
13471
13472 #if !USE_FIXED_DONT_RUN_INTO
13473   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13474
13475   /* (moved to DigField()) */
13476   if (player_can_move && DONT_RUN_INTO(element))
13477   {
13478     if (element == EL_ACID && dx == 0 && dy == 1)
13479     {
13480       SplashAcid(new_jx, new_jy);
13481       Feld[jx][jy] = EL_PLAYER_1;
13482       InitMovingField(jx, jy, MV_DOWN);
13483       Store[jx][jy] = EL_ACID;
13484       ContinueMoving(jx, jy);
13485       BuryPlayer(player);
13486     }
13487     else
13488       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13489
13490     return MP_MOVING;
13491   }
13492 #endif
13493
13494   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13495   if (can_move != MP_MOVING)
13496     return can_move;
13497
13498   /* check if DigField() has caused relocation of the player */
13499   if (player->jx != jx || player->jy != jy)
13500     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13501
13502   StorePlayer[jx][jy] = 0;
13503   player->last_jx = jx;
13504   player->last_jy = jy;
13505   player->jx = new_jx;
13506   player->jy = new_jy;
13507   StorePlayer[new_jx][new_jy] = player->element_nr;
13508
13509   if (player->move_delay_value_next != -1)
13510   {
13511     player->move_delay_value = player->move_delay_value_next;
13512     player->move_delay_value_next = -1;
13513   }
13514
13515   player->MovPos =
13516     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13517
13518   player->step_counter++;
13519
13520   PlayerVisit[jx][jy] = FrameCounter;
13521
13522 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13523   player->is_moving = TRUE;
13524 #endif
13525
13526 #if 1
13527   /* should better be called in MovePlayer(), but this breaks some tapes */
13528   ScrollPlayer(player, SCROLL_INIT);
13529 #endif
13530
13531   return MP_MOVING;
13532 }
13533
13534 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13535 {
13536   int jx = player->jx, jy = player->jy;
13537   int old_jx = jx, old_jy = jy;
13538   int moved = MP_NO_ACTION;
13539
13540   if (!player->active)
13541     return FALSE;
13542
13543   if (!dx && !dy)
13544   {
13545     if (player->MovPos == 0)
13546     {
13547       player->is_moving = FALSE;
13548       player->is_digging = FALSE;
13549       player->is_collecting = FALSE;
13550       player->is_snapping = FALSE;
13551       player->is_pushing = FALSE;
13552     }
13553
13554     return FALSE;
13555   }
13556
13557   if (player->move_delay > 0)
13558     return FALSE;
13559
13560   player->move_delay = -1;              /* set to "uninitialized" value */
13561
13562   /* store if player is automatically moved to next field */
13563   player->is_auto_moving = (player->programmed_action != MV_NONE);
13564
13565   /* remove the last programmed player action */
13566   player->programmed_action = 0;
13567
13568   if (player->MovPos)
13569   {
13570     /* should only happen if pre-1.2 tape recordings are played */
13571     /* this is only for backward compatibility */
13572
13573     int original_move_delay_value = player->move_delay_value;
13574
13575 #if DEBUG
13576     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13577            tape.counter);
13578 #endif
13579
13580     /* scroll remaining steps with finest movement resolution */
13581     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13582
13583     while (player->MovPos)
13584     {
13585       ScrollPlayer(player, SCROLL_GO_ON);
13586       ScrollScreen(NULL, SCROLL_GO_ON);
13587
13588       AdvanceFrameAndPlayerCounters(player->index_nr);
13589
13590       DrawAllPlayers();
13591       BackToFront();
13592     }
13593
13594     player->move_delay_value = original_move_delay_value;
13595   }
13596
13597   player->is_active = FALSE;
13598
13599   if (player->last_move_dir & MV_HORIZONTAL)
13600   {
13601     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13602       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13603   }
13604   else
13605   {
13606     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13607       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13608   }
13609
13610 #if USE_FIXED_BORDER_RUNNING_GFX
13611   if (!moved && !player->is_active)
13612   {
13613     player->is_moving = FALSE;
13614     player->is_digging = FALSE;
13615     player->is_collecting = FALSE;
13616     player->is_snapping = FALSE;
13617     player->is_pushing = FALSE;
13618   }
13619 #endif
13620
13621   jx = player->jx;
13622   jy = player->jy;
13623
13624 #if 1
13625   if (moved & MP_MOVING && !ScreenMovPos &&
13626       (player->index_nr == game.centered_player_nr ||
13627        game.centered_player_nr == -1))
13628 #else
13629   if (moved & MP_MOVING && !ScreenMovPos &&
13630       (player == local_player || !options.network))
13631 #endif
13632   {
13633     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13634     int offset = game.scroll_delay_value;
13635
13636     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13637     {
13638       /* actual player has left the screen -- scroll in that direction */
13639       if (jx != old_jx)         /* player has moved horizontally */
13640         scroll_x += (jx - old_jx);
13641       else                      /* player has moved vertically */
13642         scroll_y += (jy - old_jy);
13643     }
13644     else
13645     {
13646       if (jx != old_jx)         /* player has moved horizontally */
13647       {
13648         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13649             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13650           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13651
13652         /* don't scroll over playfield boundaries */
13653         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13654           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13655
13656         /* don't scroll more than one field at a time */
13657         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13658
13659         /* don't scroll against the player's moving direction */
13660         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13661             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13662           scroll_x = old_scroll_x;
13663       }
13664       else                      /* player has moved vertically */
13665       {
13666         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13667             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13668           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13669
13670         /* don't scroll over playfield boundaries */
13671         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13672           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13673
13674         /* don't scroll more than one field at a time */
13675         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13676
13677         /* don't scroll against the player's moving direction */
13678         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13679             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13680           scroll_y = old_scroll_y;
13681       }
13682     }
13683
13684     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13685     {
13686 #if 1
13687       if (!options.network && game.centered_player_nr == -1 &&
13688           !AllPlayersInVisibleScreen())
13689       {
13690         scroll_x = old_scroll_x;
13691         scroll_y = old_scroll_y;
13692       }
13693       else
13694 #else
13695       if (!options.network && !AllPlayersInVisibleScreen())
13696       {
13697         scroll_x = old_scroll_x;
13698         scroll_y = old_scroll_y;
13699       }
13700       else
13701 #endif
13702       {
13703         ScrollScreen(player, SCROLL_INIT);
13704         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13705       }
13706     }
13707   }
13708
13709   player->StepFrame = 0;
13710
13711   if (moved & MP_MOVING)
13712   {
13713     if (old_jx != jx && old_jy == jy)
13714       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13715     else if (old_jx == jx && old_jy != jy)
13716       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13717
13718     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13719
13720     player->last_move_dir = player->MovDir;
13721     player->is_moving = TRUE;
13722     player->is_snapping = FALSE;
13723     player->is_switching = FALSE;
13724     player->is_dropping = FALSE;
13725     player->is_dropping_pressed = FALSE;
13726     player->drop_pressed_delay = 0;
13727
13728 #if 0
13729     /* should better be called here than above, but this breaks some tapes */
13730     ScrollPlayer(player, SCROLL_INIT);
13731 #endif
13732   }
13733   else
13734   {
13735     CheckGravityMovementWhenNotMoving(player);
13736
13737     player->is_moving = FALSE;
13738
13739     /* at this point, the player is allowed to move, but cannot move right now
13740        (e.g. because of something blocking the way) -- ensure that the player
13741        is also allowed to move in the next frame (in old versions before 3.1.1,
13742        the player was forced to wait again for eight frames before next try) */
13743
13744     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13745       player->move_delay = 0;   /* allow direct movement in the next frame */
13746   }
13747
13748   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13749     player->move_delay = player->move_delay_value;
13750
13751   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13752   {
13753     TestIfPlayerTouchesBadThing(jx, jy);
13754     TestIfPlayerTouchesCustomElement(jx, jy);
13755   }
13756
13757   if (!player->active)
13758     RemovePlayer(player);
13759
13760   return moved;
13761 }
13762
13763 void ScrollPlayer(struct PlayerInfo *player, int mode)
13764 {
13765   int jx = player->jx, jy = player->jy;
13766   int last_jx = player->last_jx, last_jy = player->last_jy;
13767   int move_stepsize = TILEX / player->move_delay_value;
13768
13769 #if USE_NEW_PLAYER_SPEED
13770   if (!player->active)
13771     return;
13772
13773   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13774     return;
13775 #else
13776   if (!player->active || player->MovPos == 0)
13777     return;
13778 #endif
13779
13780   if (mode == SCROLL_INIT)
13781   {
13782     player->actual_frame_counter = FrameCounter;
13783     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13784
13785     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13786         Feld[last_jx][last_jy] == EL_EMPTY)
13787     {
13788       int last_field_block_delay = 0;   /* start with no blocking at all */
13789       int block_delay_adjustment = player->block_delay_adjustment;
13790
13791       /* if player blocks last field, add delay for exactly one move */
13792       if (player->block_last_field)
13793       {
13794         last_field_block_delay += player->move_delay_value;
13795
13796         /* when blocking enabled, prevent moving up despite gravity */
13797 #if USE_PLAYER_GRAVITY
13798         if (player->gravity && player->MovDir == MV_UP)
13799           block_delay_adjustment = -1;
13800 #else
13801         if (game.gravity && player->MovDir == MV_UP)
13802           block_delay_adjustment = -1;
13803 #endif
13804       }
13805
13806       /* add block delay adjustment (also possible when not blocking) */
13807       last_field_block_delay += block_delay_adjustment;
13808
13809       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13810       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13811     }
13812
13813 #if USE_NEW_PLAYER_SPEED
13814     if (player->MovPos != 0)    /* player has not yet reached destination */
13815       return;
13816 #else
13817     return;
13818 #endif
13819   }
13820   else if (!FrameReached(&player->actual_frame_counter, 1))
13821     return;
13822
13823 #if USE_NEW_PLAYER_SPEED
13824   if (player->MovPos != 0)
13825   {
13826     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13827     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13828
13829     /* before DrawPlayer() to draw correct player graphic for this case */
13830     if (player->MovPos == 0)
13831       CheckGravityMovement(player);
13832   }
13833 #else
13834   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13835   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13836
13837   /* before DrawPlayer() to draw correct player graphic for this case */
13838   if (player->MovPos == 0)
13839     CheckGravityMovement(player);
13840 #endif
13841
13842   if (player->MovPos == 0)      /* player reached destination field */
13843   {
13844     if (player->move_delay_reset_counter > 0)
13845     {
13846       player->move_delay_reset_counter--;
13847
13848       if (player->move_delay_reset_counter == 0)
13849       {
13850         /* continue with normal speed after quickly moving through gate */
13851         HALVE_PLAYER_SPEED(player);
13852
13853         /* be able to make the next move without delay */
13854         player->move_delay = 0;
13855       }
13856     }
13857
13858     player->last_jx = jx;
13859     player->last_jy = jy;
13860
13861     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13862         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13863 #if 1
13864         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13865 #endif
13866         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13867         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13868 #if 1
13869         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13870 #endif
13871         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13872         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13873     {
13874       DrawPlayer(player);       /* needed here only to cleanup last field */
13875       RemovePlayer(player);
13876
13877       if (local_player->friends_still_needed == 0 ||
13878           IS_SP_ELEMENT(Feld[jx][jy]))
13879         PlayerWins(player);
13880     }
13881
13882     /* this breaks one level: "machine", level 000 */
13883     {
13884       int move_direction = player->MovDir;
13885       int enter_side = MV_DIR_OPPOSITE(move_direction);
13886       int leave_side = move_direction;
13887       int old_jx = last_jx;
13888       int old_jy = last_jy;
13889       int old_element = Feld[old_jx][old_jy];
13890       int new_element = Feld[jx][jy];
13891
13892       if (IS_CUSTOM_ELEMENT(old_element))
13893         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13894                                    CE_LEFT_BY_PLAYER,
13895                                    player->index_bit, leave_side);
13896
13897       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13898                                           CE_PLAYER_LEAVES_X,
13899                                           player->index_bit, leave_side);
13900
13901       if (IS_CUSTOM_ELEMENT(new_element))
13902         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13903                                    player->index_bit, enter_side);
13904
13905       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13906                                           CE_PLAYER_ENTERS_X,
13907                                           player->index_bit, enter_side);
13908
13909 #if USE_FIX_CE_ACTION_WITH_PLAYER
13910       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13911                                         CE_MOVE_OF_X, move_direction);
13912 #else
13913       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13914                                         CE_MOVE_OF_X, move_direction);
13915 #endif
13916     }
13917
13918     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13919     {
13920       TestIfPlayerTouchesBadThing(jx, jy);
13921       TestIfPlayerTouchesCustomElement(jx, jy);
13922
13923       /* needed because pushed element has not yet reached its destination,
13924          so it would trigger a change event at its previous field location */
13925       if (!player->is_pushing)
13926         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13927
13928       if (!player->active)
13929         RemovePlayer(player);
13930     }
13931
13932     if (!local_player->LevelSolved && level.use_step_counter)
13933     {
13934       int i;
13935
13936       TimePlayed++;
13937
13938       if (TimeLeft > 0)
13939       {
13940         TimeLeft--;
13941
13942         if (TimeLeft <= 10 && setup.time_limit)
13943           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13944
13945 #if 1
13946         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13947
13948         DisplayGameControlValues();
13949 #else
13950         DrawGameValue_Time(TimeLeft);
13951 #endif
13952
13953         if (!TimeLeft && setup.time_limit)
13954           for (i = 0; i < MAX_PLAYERS; i++)
13955             KillPlayer(&stored_player[i]);
13956       }
13957 #if 1
13958       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13959       {
13960         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13961
13962         DisplayGameControlValues();
13963       }
13964 #else
13965       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13966         DrawGameValue_Time(TimePlayed);
13967 #endif
13968     }
13969
13970     if (tape.single_step && tape.recording && !tape.pausing &&
13971         !player->programmed_action)
13972       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13973   }
13974 }
13975
13976 void ScrollScreen(struct PlayerInfo *player, int mode)
13977 {
13978   static unsigned long screen_frame_counter = 0;
13979
13980   if (mode == SCROLL_INIT)
13981   {
13982     /* set scrolling step size according to actual player's moving speed */
13983     ScrollStepSize = TILEX / player->move_delay_value;
13984
13985     screen_frame_counter = FrameCounter;
13986     ScreenMovDir = player->MovDir;
13987     ScreenMovPos = player->MovPos;
13988     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13989     return;
13990   }
13991   else if (!FrameReached(&screen_frame_counter, 1))
13992     return;
13993
13994   if (ScreenMovPos)
13995   {
13996     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13997     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13998     redraw_mask |= REDRAW_FIELD;
13999   }
14000   else
14001     ScreenMovDir = MV_NONE;
14002 }
14003
14004 void TestIfPlayerTouchesCustomElement(int x, int y)
14005 {
14006   static int xy[4][2] =
14007   {
14008     { 0, -1 },
14009     { -1, 0 },
14010     { +1, 0 },
14011     { 0, +1 }
14012   };
14013   static int trigger_sides[4][2] =
14014   {
14015     /* center side       border side */
14016     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14017     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14018     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14019     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14020   };
14021   static int touch_dir[4] =
14022   {
14023     MV_LEFT | MV_RIGHT,
14024     MV_UP   | MV_DOWN,
14025     MV_UP   | MV_DOWN,
14026     MV_LEFT | MV_RIGHT
14027   };
14028   int center_element = Feld[x][y];      /* should always be non-moving! */
14029   int i;
14030
14031   for (i = 0; i < NUM_DIRECTIONS; i++)
14032   {
14033     int xx = x + xy[i][0];
14034     int yy = y + xy[i][1];
14035     int center_side = trigger_sides[i][0];
14036     int border_side = trigger_sides[i][1];
14037     int border_element;
14038
14039     if (!IN_LEV_FIELD(xx, yy))
14040       continue;
14041
14042     if (IS_PLAYER(x, y))                /* player found at center element */
14043     {
14044       struct PlayerInfo *player = PLAYERINFO(x, y);
14045
14046       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14047         border_element = Feld[xx][yy];          /* may be moving! */
14048       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14049         border_element = Feld[xx][yy];
14050       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14051         border_element = MovingOrBlocked2Element(xx, yy);
14052       else
14053         continue;               /* center and border element do not touch */
14054
14055       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14056                                  player->index_bit, border_side);
14057       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14058                                           CE_PLAYER_TOUCHES_X,
14059                                           player->index_bit, border_side);
14060
14061 #if USE_FIX_CE_ACTION_WITH_PLAYER
14062       {
14063         /* use player element that is initially defined in the level playfield,
14064            not the player element that corresponds to the runtime player number
14065            (example: a level that contains EL_PLAYER_3 as the only player would
14066            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14067         int player_element = PLAYERINFO(x, y)->initial_element;
14068
14069         CheckElementChangeBySide(xx, yy, border_element, player_element,
14070                                  CE_TOUCHING_X, border_side);
14071       }
14072 #endif
14073     }
14074     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14075     {
14076       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14077
14078       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14079       {
14080         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14081           continue;             /* center and border element do not touch */
14082       }
14083
14084       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14085                                  player->index_bit, center_side);
14086       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14087                                           CE_PLAYER_TOUCHES_X,
14088                                           player->index_bit, center_side);
14089
14090 #if USE_FIX_CE_ACTION_WITH_PLAYER
14091       {
14092         /* use player element that is initially defined in the level playfield,
14093            not the player element that corresponds to the runtime player number
14094            (example: a level that contains EL_PLAYER_3 as the only player would
14095            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14096         int player_element = PLAYERINFO(xx, yy)->initial_element;
14097
14098         CheckElementChangeBySide(x, y, center_element, player_element,
14099                                  CE_TOUCHING_X, center_side);
14100       }
14101 #endif
14102
14103       break;
14104     }
14105   }
14106 }
14107
14108 #if USE_ELEMENT_TOUCHING_BUGFIX
14109
14110 void TestIfElementTouchesCustomElement(int x, int y)
14111 {
14112   static int xy[4][2] =
14113   {
14114     { 0, -1 },
14115     { -1, 0 },
14116     { +1, 0 },
14117     { 0, +1 }
14118   };
14119   static int trigger_sides[4][2] =
14120   {
14121     /* center side      border side */
14122     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14123     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14124     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14125     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14126   };
14127   static int touch_dir[4] =
14128   {
14129     MV_LEFT | MV_RIGHT,
14130     MV_UP   | MV_DOWN,
14131     MV_UP   | MV_DOWN,
14132     MV_LEFT | MV_RIGHT
14133   };
14134   boolean change_center_element = FALSE;
14135   int center_element = Feld[x][y];      /* should always be non-moving! */
14136   int border_element_old[NUM_DIRECTIONS];
14137   int i;
14138
14139   for (i = 0; i < NUM_DIRECTIONS; i++)
14140   {
14141     int xx = x + xy[i][0];
14142     int yy = y + xy[i][1];
14143     int border_element;
14144
14145     border_element_old[i] = -1;
14146
14147     if (!IN_LEV_FIELD(xx, yy))
14148       continue;
14149
14150     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14151       border_element = Feld[xx][yy];    /* may be moving! */
14152     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14153       border_element = Feld[xx][yy];
14154     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14155       border_element = MovingOrBlocked2Element(xx, yy);
14156     else
14157       continue;                 /* center and border element do not touch */
14158
14159     border_element_old[i] = border_element;
14160   }
14161
14162   for (i = 0; i < NUM_DIRECTIONS; i++)
14163   {
14164     int xx = x + xy[i][0];
14165     int yy = y + xy[i][1];
14166     int center_side = trigger_sides[i][0];
14167     int border_element = border_element_old[i];
14168
14169     if (border_element == -1)
14170       continue;
14171
14172     /* check for change of border element */
14173     CheckElementChangeBySide(xx, yy, border_element, center_element,
14174                              CE_TOUCHING_X, center_side);
14175
14176     /* (center element cannot be player, so we dont have to check this here) */
14177   }
14178
14179   for (i = 0; i < NUM_DIRECTIONS; i++)
14180   {
14181     int xx = x + xy[i][0];
14182     int yy = y + xy[i][1];
14183     int border_side = trigger_sides[i][1];
14184     int border_element = border_element_old[i];
14185
14186     if (border_element == -1)
14187       continue;
14188
14189     /* check for change of center element (but change it only once) */
14190     if (!change_center_element)
14191       change_center_element =
14192         CheckElementChangeBySide(x, y, center_element, border_element,
14193                                  CE_TOUCHING_X, border_side);
14194
14195 #if USE_FIX_CE_ACTION_WITH_PLAYER
14196     if (IS_PLAYER(xx, yy))
14197     {
14198       /* use player element that is initially defined in the level playfield,
14199          not the player element that corresponds to the runtime player number
14200          (example: a level that contains EL_PLAYER_3 as the only player would
14201          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14202       int player_element = PLAYERINFO(xx, yy)->initial_element;
14203
14204       CheckElementChangeBySide(x, y, center_element, player_element,
14205                                CE_TOUCHING_X, border_side);
14206     }
14207 #endif
14208   }
14209 }
14210
14211 #else
14212
14213 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14214 {
14215   static int xy[4][2] =
14216   {
14217     { 0, -1 },
14218     { -1, 0 },
14219     { +1, 0 },
14220     { 0, +1 }
14221   };
14222   static int trigger_sides[4][2] =
14223   {
14224     /* center side      border side */
14225     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14226     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14227     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14228     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14229   };
14230   static int touch_dir[4] =
14231   {
14232     MV_LEFT | MV_RIGHT,
14233     MV_UP   | MV_DOWN,
14234     MV_UP   | MV_DOWN,
14235     MV_LEFT | MV_RIGHT
14236   };
14237   boolean change_center_element = FALSE;
14238   int center_element = Feld[x][y];      /* should always be non-moving! */
14239   int i;
14240
14241   for (i = 0; i < NUM_DIRECTIONS; i++)
14242   {
14243     int xx = x + xy[i][0];
14244     int yy = y + xy[i][1];
14245     int center_side = trigger_sides[i][0];
14246     int border_side = trigger_sides[i][1];
14247     int border_element;
14248
14249     if (!IN_LEV_FIELD(xx, yy))
14250       continue;
14251
14252     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14253       border_element = Feld[xx][yy];    /* may be moving! */
14254     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14255       border_element = Feld[xx][yy];
14256     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14257       border_element = MovingOrBlocked2Element(xx, yy);
14258     else
14259       continue;                 /* center and border element do not touch */
14260
14261     /* check for change of center element (but change it only once) */
14262     if (!change_center_element)
14263       change_center_element =
14264         CheckElementChangeBySide(x, y, center_element, border_element,
14265                                  CE_TOUCHING_X, border_side);
14266
14267     /* check for change of border element */
14268     CheckElementChangeBySide(xx, yy, border_element, center_element,
14269                              CE_TOUCHING_X, center_side);
14270   }
14271 }
14272
14273 #endif
14274
14275 void TestIfElementHitsCustomElement(int x, int y, int direction)
14276 {
14277   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14278   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14279   int hitx = x + dx, hity = y + dy;
14280   int hitting_element = Feld[x][y];
14281   int touched_element;
14282
14283   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14284     return;
14285
14286   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14287                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14288
14289   if (IN_LEV_FIELD(hitx, hity))
14290   {
14291     int opposite_direction = MV_DIR_OPPOSITE(direction);
14292     int hitting_side = direction;
14293     int touched_side = opposite_direction;
14294     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14295                           MovDir[hitx][hity] != direction ||
14296                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14297
14298     object_hit = TRUE;
14299
14300     if (object_hit)
14301     {
14302       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14303                                CE_HITTING_X, touched_side);
14304
14305       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14306                                CE_HIT_BY_X, hitting_side);
14307
14308       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14309                                CE_HIT_BY_SOMETHING, opposite_direction);
14310
14311 #if USE_FIX_CE_ACTION_WITH_PLAYER
14312       if (IS_PLAYER(hitx, hity))
14313       {
14314         /* use player element that is initially defined in the level playfield,
14315            not the player element that corresponds to the runtime player number
14316            (example: a level that contains EL_PLAYER_3 as the only player would
14317            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14318         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14319
14320         CheckElementChangeBySide(x, y, hitting_element, player_element,
14321                                  CE_HITTING_X, touched_side);
14322       }
14323 #endif
14324     }
14325   }
14326
14327   /* "hitting something" is also true when hitting the playfield border */
14328   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14329                            CE_HITTING_SOMETHING, direction);
14330 }
14331
14332 #if 0
14333 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14334 {
14335   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14336   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14337   int hitx = x + dx, hity = y + dy;
14338   int hitting_element = Feld[x][y];
14339   int touched_element;
14340 #if 0
14341   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14342                         !IS_FREE(hitx, hity) &&
14343                         (!IS_MOVING(hitx, hity) ||
14344                          MovDir[hitx][hity] != direction ||
14345                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14346 #endif
14347
14348   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14349     return;
14350
14351 #if 0
14352   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14353     return;
14354 #endif
14355
14356   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14357                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14358
14359   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14360                            EP_CAN_SMASH_EVERYTHING, direction);
14361
14362   if (IN_LEV_FIELD(hitx, hity))
14363   {
14364     int opposite_direction = MV_DIR_OPPOSITE(direction);
14365     int hitting_side = direction;
14366     int touched_side = opposite_direction;
14367 #if 0
14368     int touched_element = MovingOrBlocked2Element(hitx, hity);
14369 #endif
14370 #if 1
14371     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14372                           MovDir[hitx][hity] != direction ||
14373                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14374
14375     object_hit = TRUE;
14376 #endif
14377
14378     if (object_hit)
14379     {
14380       int i;
14381
14382       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14383                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14384
14385       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14386                                CE_OTHER_IS_SMASHING, touched_side);
14387
14388       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14389                                CE_OTHER_GETS_SMASHED, hitting_side);
14390     }
14391   }
14392 }
14393 #endif
14394
14395 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14396 {
14397   int i, kill_x = -1, kill_y = -1;
14398
14399   int bad_element = -1;
14400   static int test_xy[4][2] =
14401   {
14402     { 0, -1 },
14403     { -1, 0 },
14404     { +1, 0 },
14405     { 0, +1 }
14406   };
14407   static int test_dir[4] =
14408   {
14409     MV_UP,
14410     MV_LEFT,
14411     MV_RIGHT,
14412     MV_DOWN
14413   };
14414
14415   for (i = 0; i < NUM_DIRECTIONS; i++)
14416   {
14417     int test_x, test_y, test_move_dir, test_element;
14418
14419     test_x = good_x + test_xy[i][0];
14420     test_y = good_y + test_xy[i][1];
14421
14422     if (!IN_LEV_FIELD(test_x, test_y))
14423       continue;
14424
14425     test_move_dir =
14426       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14427
14428     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14429
14430     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14431        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14432     */
14433     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14434         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14435     {
14436       kill_x = test_x;
14437       kill_y = test_y;
14438       bad_element = test_element;
14439
14440       break;
14441     }
14442   }
14443
14444   if (kill_x != -1 || kill_y != -1)
14445   {
14446     if (IS_PLAYER(good_x, good_y))
14447     {
14448       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14449
14450       if (player->shield_deadly_time_left > 0 &&
14451           !IS_INDESTRUCTIBLE(bad_element))
14452         Bang(kill_x, kill_y);
14453       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14454         KillPlayer(player);
14455     }
14456     else
14457       Bang(good_x, good_y);
14458   }
14459 }
14460
14461 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14462 {
14463   int i, kill_x = -1, kill_y = -1;
14464   int bad_element = Feld[bad_x][bad_y];
14465   static int test_xy[4][2] =
14466   {
14467     { 0, -1 },
14468     { -1, 0 },
14469     { +1, 0 },
14470     { 0, +1 }
14471   };
14472   static int touch_dir[4] =
14473   {
14474     MV_LEFT | MV_RIGHT,
14475     MV_UP   | MV_DOWN,
14476     MV_UP   | MV_DOWN,
14477     MV_LEFT | MV_RIGHT
14478   };
14479   static int test_dir[4] =
14480   {
14481     MV_UP,
14482     MV_LEFT,
14483     MV_RIGHT,
14484     MV_DOWN
14485   };
14486
14487   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14488     return;
14489
14490   for (i = 0; i < NUM_DIRECTIONS; i++)
14491   {
14492     int test_x, test_y, test_move_dir, test_element;
14493
14494     test_x = bad_x + test_xy[i][0];
14495     test_y = bad_y + test_xy[i][1];
14496
14497     if (!IN_LEV_FIELD(test_x, test_y))
14498       continue;
14499
14500     test_move_dir =
14501       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14502
14503     test_element = Feld[test_x][test_y];
14504
14505     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14506        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14507     */
14508     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14509         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14510     {
14511       /* good thing is player or penguin that does not move away */
14512       if (IS_PLAYER(test_x, test_y))
14513       {
14514         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14515
14516         if (bad_element == EL_ROBOT && player->is_moving)
14517           continue;     /* robot does not kill player if he is moving */
14518
14519         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14520         {
14521           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14522             continue;           /* center and border element do not touch */
14523         }
14524
14525         kill_x = test_x;
14526         kill_y = test_y;
14527
14528         break;
14529       }
14530       else if (test_element == EL_PENGUIN)
14531       {
14532         kill_x = test_x;
14533         kill_y = test_y;
14534
14535         break;
14536       }
14537     }
14538   }
14539
14540   if (kill_x != -1 || kill_y != -1)
14541   {
14542     if (IS_PLAYER(kill_x, kill_y))
14543     {
14544       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14545
14546       if (player->shield_deadly_time_left > 0 &&
14547           !IS_INDESTRUCTIBLE(bad_element))
14548         Bang(bad_x, bad_y);
14549       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14550         KillPlayer(player);
14551     }
14552     else
14553       Bang(kill_x, kill_y);
14554   }
14555 }
14556
14557 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14558 {
14559   int bad_element = Feld[bad_x][bad_y];
14560   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14561   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14562   int test_x = bad_x + dx, test_y = bad_y + dy;
14563   int test_move_dir, test_element;
14564   int kill_x = -1, kill_y = -1;
14565
14566   if (!IN_LEV_FIELD(test_x, test_y))
14567     return;
14568
14569   test_move_dir =
14570     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14571
14572   test_element = Feld[test_x][test_y];
14573
14574   if (test_move_dir != bad_move_dir)
14575   {
14576     /* good thing can be player or penguin that does not move away */
14577     if (IS_PLAYER(test_x, test_y))
14578     {
14579       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14580
14581       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14582          player as being hit when he is moving towards the bad thing, because
14583          the "get hit by" condition would be lost after the player stops) */
14584       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14585         return;         /* player moves away from bad thing */
14586
14587       kill_x = test_x;
14588       kill_y = test_y;
14589     }
14590     else if (test_element == EL_PENGUIN)
14591     {
14592       kill_x = test_x;
14593       kill_y = test_y;
14594     }
14595   }
14596
14597   if (kill_x != -1 || kill_y != -1)
14598   {
14599     if (IS_PLAYER(kill_x, kill_y))
14600     {
14601       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14602
14603       if (player->shield_deadly_time_left > 0 &&
14604           !IS_INDESTRUCTIBLE(bad_element))
14605         Bang(bad_x, bad_y);
14606       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14607         KillPlayer(player);
14608     }
14609     else
14610       Bang(kill_x, kill_y);
14611   }
14612 }
14613
14614 void TestIfPlayerTouchesBadThing(int x, int y)
14615 {
14616   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14617 }
14618
14619 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14620 {
14621   TestIfGoodThingHitsBadThing(x, y, move_dir);
14622 }
14623
14624 void TestIfBadThingTouchesPlayer(int x, int y)
14625 {
14626   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14627 }
14628
14629 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14630 {
14631   TestIfBadThingHitsGoodThing(x, y, move_dir);
14632 }
14633
14634 void TestIfFriendTouchesBadThing(int x, int y)
14635 {
14636   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14637 }
14638
14639 void TestIfBadThingTouchesFriend(int x, int y)
14640 {
14641   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14642 }
14643
14644 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14645 {
14646   int i, kill_x = bad_x, kill_y = bad_y;
14647   static int xy[4][2] =
14648   {
14649     { 0, -1 },
14650     { -1, 0 },
14651     { +1, 0 },
14652     { 0, +1 }
14653   };
14654
14655   for (i = 0; i < NUM_DIRECTIONS; i++)
14656   {
14657     int x, y, element;
14658
14659     x = bad_x + xy[i][0];
14660     y = bad_y + xy[i][1];
14661     if (!IN_LEV_FIELD(x, y))
14662       continue;
14663
14664     element = Feld[x][y];
14665     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14666         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14667     {
14668       kill_x = x;
14669       kill_y = y;
14670       break;
14671     }
14672   }
14673
14674   if (kill_x != bad_x || kill_y != bad_y)
14675     Bang(bad_x, bad_y);
14676 }
14677
14678 void KillPlayer(struct PlayerInfo *player)
14679 {
14680   int jx = player->jx, jy = player->jy;
14681
14682   if (!player->active)
14683     return;
14684
14685 #if 0
14686   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14687          player->killed, player->active, player->reanimated);
14688 #endif
14689
14690   /* the following code was introduced to prevent an infinite loop when calling
14691      -> Bang()
14692      -> CheckTriggeredElementChangeExt()
14693      -> ExecuteCustomElementAction()
14694      -> KillPlayer()
14695      -> (infinitely repeating the above sequence of function calls)
14696      which occurs when killing the player while having a CE with the setting
14697      "kill player X when explosion of <player X>"; the solution using a new
14698      field "player->killed" was chosen for backwards compatibility, although
14699      clever use of the fields "player->active" etc. would probably also work */
14700 #if 1
14701   if (player->killed)
14702     return;
14703 #endif
14704
14705   player->killed = TRUE;
14706
14707   /* remove accessible field at the player's position */
14708   Feld[jx][jy] = EL_EMPTY;
14709
14710   /* deactivate shield (else Bang()/Explode() would not work right) */
14711   player->shield_normal_time_left = 0;
14712   player->shield_deadly_time_left = 0;
14713
14714 #if 0
14715   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14716          player->killed, player->active, player->reanimated);
14717 #endif
14718
14719   Bang(jx, jy);
14720
14721 #if 0
14722   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14723          player->killed, player->active, player->reanimated);
14724 #endif
14725
14726 #if USE_PLAYER_REANIMATION
14727 #if 1
14728   if (player->reanimated)       /* killed player may have been reanimated */
14729     player->killed = player->reanimated = FALSE;
14730   else
14731     BuryPlayer(player);
14732 #else
14733   if (player->killed)           /* player may have been reanimated */
14734     BuryPlayer(player);
14735 #endif
14736 #else
14737   BuryPlayer(player);
14738 #endif
14739 }
14740
14741 static void KillPlayerUnlessEnemyProtected(int x, int y)
14742 {
14743   if (!PLAYER_ENEMY_PROTECTED(x, y))
14744     KillPlayer(PLAYERINFO(x, y));
14745 }
14746
14747 static void KillPlayerUnlessExplosionProtected(int x, int y)
14748 {
14749   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14750     KillPlayer(PLAYERINFO(x, y));
14751 }
14752
14753 void BuryPlayer(struct PlayerInfo *player)
14754 {
14755   int jx = player->jx, jy = player->jy;
14756
14757   if (!player->active)
14758     return;
14759
14760   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14761   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14762
14763   player->GameOver = TRUE;
14764   RemovePlayer(player);
14765 }
14766
14767 void RemovePlayer(struct PlayerInfo *player)
14768 {
14769   int jx = player->jx, jy = player->jy;
14770   int i, found = FALSE;
14771
14772   player->present = FALSE;
14773   player->active = FALSE;
14774
14775   if (!ExplodeField[jx][jy])
14776     StorePlayer[jx][jy] = 0;
14777
14778   if (player->is_moving)
14779     TEST_DrawLevelField(player->last_jx, player->last_jy);
14780
14781   for (i = 0; i < MAX_PLAYERS; i++)
14782     if (stored_player[i].active)
14783       found = TRUE;
14784
14785   if (!found)
14786     AllPlayersGone = TRUE;
14787
14788   ExitX = ZX = jx;
14789   ExitY = ZY = jy;
14790 }
14791
14792 #if USE_NEW_SNAP_DELAY
14793 static void setFieldForSnapping(int x, int y, int element, int direction)
14794 {
14795   struct ElementInfo *ei = &element_info[element];
14796   int direction_bit = MV_DIR_TO_BIT(direction);
14797   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14798   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14799                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14800
14801   Feld[x][y] = EL_ELEMENT_SNAPPING;
14802   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14803
14804   ResetGfxAnimation(x, y);
14805
14806   GfxElement[x][y] = element;
14807   GfxAction[x][y] = action;
14808   GfxDir[x][y] = direction;
14809   GfxFrame[x][y] = -1;
14810 }
14811 #endif
14812
14813 /*
14814   =============================================================================
14815   checkDiagonalPushing()
14816   -----------------------------------------------------------------------------
14817   check if diagonal input device direction results in pushing of object
14818   (by checking if the alternative direction is walkable, diggable, ...)
14819   =============================================================================
14820 */
14821
14822 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14823                                     int x, int y, int real_dx, int real_dy)
14824 {
14825   int jx, jy, dx, dy, xx, yy;
14826
14827   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14828     return TRUE;
14829
14830   /* diagonal direction: check alternative direction */
14831   jx = player->jx;
14832   jy = player->jy;
14833   dx = x - jx;
14834   dy = y - jy;
14835   xx = jx + (dx == 0 ? real_dx : 0);
14836   yy = jy + (dy == 0 ? real_dy : 0);
14837
14838   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14839 }
14840
14841 /*
14842   =============================================================================
14843   DigField()
14844   -----------------------------------------------------------------------------
14845   x, y:                 field next to player (non-diagonal) to try to dig to
14846   real_dx, real_dy:     direction as read from input device (can be diagonal)
14847   =============================================================================
14848 */
14849
14850 static int DigField(struct PlayerInfo *player,
14851                     int oldx, int oldy, int x, int y,
14852                     int real_dx, int real_dy, int mode)
14853 {
14854   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14855   boolean player_was_pushing = player->is_pushing;
14856   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14857   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14858   int jx = oldx, jy = oldy;
14859   int dx = x - jx, dy = y - jy;
14860   int nextx = x + dx, nexty = y + dy;
14861   int move_direction = (dx == -1 ? MV_LEFT  :
14862                         dx == +1 ? MV_RIGHT :
14863                         dy == -1 ? MV_UP    :
14864                         dy == +1 ? MV_DOWN  : MV_NONE);
14865   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14866   int dig_side = MV_DIR_OPPOSITE(move_direction);
14867   int old_element = Feld[jx][jy];
14868 #if USE_FIXED_DONT_RUN_INTO
14869   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14870 #else
14871   int element;
14872 #endif
14873   int collect_count;
14874
14875   if (is_player)                /* function can also be called by EL_PENGUIN */
14876   {
14877     if (player->MovPos == 0)
14878     {
14879       player->is_digging = FALSE;
14880       player->is_collecting = FALSE;
14881     }
14882
14883     if (player->MovPos == 0)    /* last pushing move finished */
14884       player->is_pushing = FALSE;
14885
14886     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14887     {
14888       player->is_switching = FALSE;
14889       player->push_delay = -1;
14890
14891       return MP_NO_ACTION;
14892     }
14893   }
14894
14895 #if !USE_FIXED_DONT_RUN_INTO
14896   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14897     return MP_NO_ACTION;
14898 #endif
14899
14900   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14901     old_element = Back[jx][jy];
14902
14903   /* in case of element dropped at player position, check background */
14904   else if (Back[jx][jy] != EL_EMPTY &&
14905            game.engine_version >= VERSION_IDENT(2,2,0,0))
14906     old_element = Back[jx][jy];
14907
14908   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14909     return MP_NO_ACTION;        /* field has no opening in this direction */
14910
14911   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14912     return MP_NO_ACTION;        /* field has no opening in this direction */
14913
14914 #if USE_FIXED_DONT_RUN_INTO
14915   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14916   {
14917     SplashAcid(x, y);
14918
14919     Feld[jx][jy] = player->artwork_element;
14920     InitMovingField(jx, jy, MV_DOWN);
14921     Store[jx][jy] = EL_ACID;
14922     ContinueMoving(jx, jy);
14923     BuryPlayer(player);
14924
14925     return MP_DONT_RUN_INTO;
14926   }
14927 #endif
14928
14929 #if USE_FIXED_DONT_RUN_INTO
14930   if (player_can_move && DONT_RUN_INTO(element))
14931   {
14932     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14933
14934     return MP_DONT_RUN_INTO;
14935   }
14936 #endif
14937
14938 #if USE_FIXED_DONT_RUN_INTO
14939   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14940     return MP_NO_ACTION;
14941 #endif
14942
14943 #if !USE_FIXED_DONT_RUN_INTO
14944   element = Feld[x][y];
14945 #endif
14946
14947   collect_count = element_info[element].collect_count_initial;
14948
14949   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14950     return MP_NO_ACTION;
14951
14952   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14953     player_can_move = player_can_move_or_snap;
14954
14955   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14956       game.engine_version >= VERSION_IDENT(2,2,0,0))
14957   {
14958     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14959                                player->index_bit, dig_side);
14960     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14961                                         player->index_bit, dig_side);
14962
14963     if (element == EL_DC_LANDMINE)
14964       Bang(x, y);
14965
14966     if (Feld[x][y] != element)          /* field changed by snapping */
14967       return MP_ACTION;
14968
14969     return MP_NO_ACTION;
14970   }
14971
14972 #if USE_PLAYER_GRAVITY
14973   if (player->gravity && is_player && !player->is_auto_moving &&
14974       canFallDown(player) && move_direction != MV_DOWN &&
14975       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14976     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14977 #else
14978   if (game.gravity && is_player && !player->is_auto_moving &&
14979       canFallDown(player) && move_direction != MV_DOWN &&
14980       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14981     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14982 #endif
14983
14984   if (player_can_move &&
14985       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14986   {
14987     int sound_element = SND_ELEMENT(element);
14988     int sound_action = ACTION_WALKING;
14989
14990     if (IS_RND_GATE(element))
14991     {
14992       if (!player->key[RND_GATE_NR(element)])
14993         return MP_NO_ACTION;
14994     }
14995     else if (IS_RND_GATE_GRAY(element))
14996     {
14997       if (!player->key[RND_GATE_GRAY_NR(element)])
14998         return MP_NO_ACTION;
14999     }
15000     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15001     {
15002       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15003         return MP_NO_ACTION;
15004     }
15005     else if (element == EL_EXIT_OPEN ||
15006              element == EL_EM_EXIT_OPEN ||
15007 #if 1
15008              element == EL_EM_EXIT_OPENING ||
15009 #endif
15010              element == EL_STEEL_EXIT_OPEN ||
15011              element == EL_EM_STEEL_EXIT_OPEN ||
15012 #if 1
15013              element == EL_EM_STEEL_EXIT_OPENING ||
15014 #endif
15015              element == EL_SP_EXIT_OPEN ||
15016              element == EL_SP_EXIT_OPENING)
15017     {
15018       sound_action = ACTION_PASSING;    /* player is passing exit */
15019     }
15020     else if (element == EL_EMPTY)
15021     {
15022       sound_action = ACTION_MOVING;             /* nothing to walk on */
15023     }
15024
15025     /* play sound from background or player, whatever is available */
15026     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15027       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15028     else
15029       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15030   }
15031   else if (player_can_move &&
15032            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15033   {
15034     if (!ACCESS_FROM(element, opposite_direction))
15035       return MP_NO_ACTION;      /* field not accessible from this direction */
15036
15037     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15038       return MP_NO_ACTION;
15039
15040     if (IS_EM_GATE(element))
15041     {
15042       if (!player->key[EM_GATE_NR(element)])
15043         return MP_NO_ACTION;
15044     }
15045     else if (IS_EM_GATE_GRAY(element))
15046     {
15047       if (!player->key[EM_GATE_GRAY_NR(element)])
15048         return MP_NO_ACTION;
15049     }
15050     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15051     {
15052       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15053         return MP_NO_ACTION;
15054     }
15055     else if (IS_EMC_GATE(element))
15056     {
15057       if (!player->key[EMC_GATE_NR(element)])
15058         return MP_NO_ACTION;
15059     }
15060     else if (IS_EMC_GATE_GRAY(element))
15061     {
15062       if (!player->key[EMC_GATE_GRAY_NR(element)])
15063         return MP_NO_ACTION;
15064     }
15065     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15066     {
15067       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15068         return MP_NO_ACTION;
15069     }
15070     else if (element == EL_DC_GATE_WHITE ||
15071              element == EL_DC_GATE_WHITE_GRAY ||
15072              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15073     {
15074       if (player->num_white_keys == 0)
15075         return MP_NO_ACTION;
15076
15077       player->num_white_keys--;
15078     }
15079     else if (IS_SP_PORT(element))
15080     {
15081       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15082           element == EL_SP_GRAVITY_PORT_RIGHT ||
15083           element == EL_SP_GRAVITY_PORT_UP ||
15084           element == EL_SP_GRAVITY_PORT_DOWN)
15085 #if USE_PLAYER_GRAVITY
15086         player->gravity = !player->gravity;
15087 #else
15088         game.gravity = !game.gravity;
15089 #endif
15090       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15091                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15092                element == EL_SP_GRAVITY_ON_PORT_UP ||
15093                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095         player->gravity = TRUE;
15096 #else
15097         game.gravity = TRUE;
15098 #endif
15099       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15100                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15101                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15102                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15103 #if USE_PLAYER_GRAVITY
15104         player->gravity = FALSE;
15105 #else
15106         game.gravity = FALSE;
15107 #endif
15108     }
15109
15110     /* automatically move to the next field with double speed */
15111     player->programmed_action = move_direction;
15112
15113     if (player->move_delay_reset_counter == 0)
15114     {
15115       player->move_delay_reset_counter = 2;     /* two double speed steps */
15116
15117       DOUBLE_PLAYER_SPEED(player);
15118     }
15119
15120     PlayLevelSoundAction(x, y, ACTION_PASSING);
15121   }
15122   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15123   {
15124     RemoveField(x, y);
15125
15126     if (mode != DF_SNAP)
15127     {
15128       GfxElement[x][y] = GFX_ELEMENT(element);
15129       player->is_digging = TRUE;
15130     }
15131
15132     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15133
15134     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15135                                         player->index_bit, dig_side);
15136
15137     if (mode == DF_SNAP)
15138     {
15139 #if USE_NEW_SNAP_DELAY
15140       if (level.block_snap_field)
15141         setFieldForSnapping(x, y, element, move_direction);
15142       else
15143         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15144 #else
15145       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15146 #endif
15147
15148       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15149                                           player->index_bit, dig_side);
15150     }
15151   }
15152   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15153   {
15154     RemoveField(x, y);
15155
15156     if (is_player && mode != DF_SNAP)
15157     {
15158       GfxElement[x][y] = element;
15159       player->is_collecting = TRUE;
15160     }
15161
15162     if (element == EL_SPEED_PILL)
15163     {
15164       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15165     }
15166     else if (element == EL_EXTRA_TIME && level.time > 0)
15167     {
15168       TimeLeft += level.extra_time;
15169
15170 #if 1
15171       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15172
15173       DisplayGameControlValues();
15174 #else
15175       DrawGameValue_Time(TimeLeft);
15176 #endif
15177     }
15178     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15179     {
15180       player->shield_normal_time_left += level.shield_normal_time;
15181       if (element == EL_SHIELD_DEADLY)
15182         player->shield_deadly_time_left += level.shield_deadly_time;
15183     }
15184     else if (element == EL_DYNAMITE ||
15185              element == EL_EM_DYNAMITE ||
15186              element == EL_SP_DISK_RED)
15187     {
15188       if (player->inventory_size < MAX_INVENTORY_SIZE)
15189         player->inventory_element[player->inventory_size++] = element;
15190
15191       DrawGameDoorValues();
15192     }
15193     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15194     {
15195       player->dynabomb_count++;
15196       player->dynabombs_left++;
15197     }
15198     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15199     {
15200       player->dynabomb_size++;
15201     }
15202     else if (element == EL_DYNABOMB_INCREASE_POWER)
15203     {
15204       player->dynabomb_xl = TRUE;
15205     }
15206     else if (IS_KEY(element))
15207     {
15208       player->key[KEY_NR(element)] = TRUE;
15209
15210       DrawGameDoorValues();
15211     }
15212     else if (element == EL_DC_KEY_WHITE)
15213     {
15214       player->num_white_keys++;
15215
15216       /* display white keys? */
15217       /* DrawGameDoorValues(); */
15218     }
15219     else if (IS_ENVELOPE(element))
15220     {
15221       player->show_envelope = element;
15222     }
15223     else if (element == EL_EMC_LENSES)
15224     {
15225       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15226
15227       RedrawAllInvisibleElementsForLenses();
15228     }
15229     else if (element == EL_EMC_MAGNIFIER)
15230     {
15231       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15232
15233       RedrawAllInvisibleElementsForMagnifier();
15234     }
15235     else if (IS_DROPPABLE(element) ||
15236              IS_THROWABLE(element))     /* can be collected and dropped */
15237     {
15238       int i;
15239
15240       if (collect_count == 0)
15241         player->inventory_infinite_element = element;
15242       else
15243         for (i = 0; i < collect_count; i++)
15244           if (player->inventory_size < MAX_INVENTORY_SIZE)
15245             player->inventory_element[player->inventory_size++] = element;
15246
15247       DrawGameDoorValues();
15248     }
15249     else if (collect_count > 0)
15250     {
15251       local_player->gems_still_needed -= collect_count;
15252       if (local_player->gems_still_needed < 0)
15253         local_player->gems_still_needed = 0;
15254
15255 #if 1
15256       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15257
15258       DisplayGameControlValues();
15259 #else
15260       DrawGameValue_Emeralds(local_player->gems_still_needed);
15261 #endif
15262     }
15263
15264     RaiseScoreElement(element);
15265     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15266
15267     if (is_player)
15268       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15269                                           player->index_bit, dig_side);
15270
15271     if (mode == DF_SNAP)
15272     {
15273 #if USE_NEW_SNAP_DELAY
15274       if (level.block_snap_field)
15275         setFieldForSnapping(x, y, element, move_direction);
15276       else
15277         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15278 #else
15279       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15280 #endif
15281
15282       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15283                                           player->index_bit, dig_side);
15284     }
15285   }
15286   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15287   {
15288     if (mode == DF_SNAP && element != EL_BD_ROCK)
15289       return MP_NO_ACTION;
15290
15291     if (CAN_FALL(element) && dy)
15292       return MP_NO_ACTION;
15293
15294     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15295         !(element == EL_SPRING && level.use_spring_bug))
15296       return MP_NO_ACTION;
15297
15298     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15299         ((move_direction & MV_VERTICAL &&
15300           ((element_info[element].move_pattern & MV_LEFT &&
15301             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15302            (element_info[element].move_pattern & MV_RIGHT &&
15303             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15304          (move_direction & MV_HORIZONTAL &&
15305           ((element_info[element].move_pattern & MV_UP &&
15306             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15307            (element_info[element].move_pattern & MV_DOWN &&
15308             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15309       return MP_NO_ACTION;
15310
15311     /* do not push elements already moving away faster than player */
15312     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15313         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15314       return MP_NO_ACTION;
15315
15316     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15317     {
15318       if (player->push_delay_value == -1 || !player_was_pushing)
15319         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15320     }
15321     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15322     {
15323       if (player->push_delay_value == -1)
15324         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15325     }
15326     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15327     {
15328       if (!player->is_pushing)
15329         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15330     }
15331
15332     player->is_pushing = TRUE;
15333     player->is_active = TRUE;
15334
15335     if (!(IN_LEV_FIELD(nextx, nexty) &&
15336           (IS_FREE(nextx, nexty) ||
15337            (IS_SB_ELEMENT(element) &&
15338             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15339            (IS_CUSTOM_ELEMENT(element) &&
15340             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15341       return MP_NO_ACTION;
15342
15343     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15344       return MP_NO_ACTION;
15345
15346     if (player->push_delay == -1)       /* new pushing; restart delay */
15347       player->push_delay = 0;
15348
15349     if (player->push_delay < player->push_delay_value &&
15350         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15351         element != EL_SPRING && element != EL_BALLOON)
15352     {
15353       /* make sure that there is no move delay before next try to push */
15354       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15355         player->move_delay = 0;
15356
15357       return MP_NO_ACTION;
15358     }
15359
15360     if (IS_CUSTOM_ELEMENT(element) &&
15361         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15362     {
15363       if (!DigFieldByCE(nextx, nexty, element))
15364         return MP_NO_ACTION;
15365     }
15366
15367     if (IS_SB_ELEMENT(element))
15368     {
15369       if (element == EL_SOKOBAN_FIELD_FULL)
15370       {
15371         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15372         local_player->sokobanfields_still_needed++;
15373       }
15374
15375       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15376       {
15377         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15378         local_player->sokobanfields_still_needed--;
15379       }
15380
15381       Feld[x][y] = EL_SOKOBAN_OBJECT;
15382
15383       if (Back[x][y] == Back[nextx][nexty])
15384         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15385       else if (Back[x][y] != 0)
15386         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15387                                     ACTION_EMPTYING);
15388       else
15389         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15390                                     ACTION_FILLING);
15391
15392 #if 1
15393       if (local_player->sokobanfields_still_needed == 0 &&
15394           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15395 #else
15396       if (local_player->sokobanfields_still_needed == 0 &&
15397           game.emulation == EMU_SOKOBAN)
15398 #endif
15399       {
15400         PlayerWins(player);
15401
15402         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15403       }
15404     }
15405     else
15406       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15407
15408     InitMovingField(x, y, move_direction);
15409     GfxAction[x][y] = ACTION_PUSHING;
15410
15411     if (mode == DF_SNAP)
15412       ContinueMoving(x, y);
15413     else
15414       MovPos[x][y] = (dx != 0 ? dx : dy);
15415
15416     Pushed[x][y] = TRUE;
15417     Pushed[nextx][nexty] = TRUE;
15418
15419     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15420       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15421     else
15422       player->push_delay_value = -1;    /* get new value later */
15423
15424     /* check for element change _after_ element has been pushed */
15425     if (game.use_change_when_pushing_bug)
15426     {
15427       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15428                                  player->index_bit, dig_side);
15429       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15430                                           player->index_bit, dig_side);
15431     }
15432   }
15433   else if (IS_SWITCHABLE(element))
15434   {
15435     if (PLAYER_SWITCHING(player, x, y))
15436     {
15437       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15438                                           player->index_bit, dig_side);
15439
15440       return MP_ACTION;
15441     }
15442
15443     player->is_switching = TRUE;
15444     player->switch_x = x;
15445     player->switch_y = y;
15446
15447     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15448
15449     if (element == EL_ROBOT_WHEEL)
15450     {
15451       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15452       ZX = x;
15453       ZY = y;
15454
15455       game.robot_wheel_active = TRUE;
15456
15457       TEST_DrawLevelField(x, y);
15458     }
15459     else if (element == EL_SP_TERMINAL)
15460     {
15461       int xx, yy;
15462
15463       SCAN_PLAYFIELD(xx, yy)
15464       {
15465         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15466           Bang(xx, yy);
15467         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15468           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15469       }
15470     }
15471     else if (IS_BELT_SWITCH(element))
15472     {
15473       ToggleBeltSwitch(x, y);
15474     }
15475     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15476              element == EL_SWITCHGATE_SWITCH_DOWN ||
15477              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15478              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15479     {
15480       ToggleSwitchgateSwitch(x, y);
15481     }
15482     else if (element == EL_LIGHT_SWITCH ||
15483              element == EL_LIGHT_SWITCH_ACTIVE)
15484     {
15485       ToggleLightSwitch(x, y);
15486     }
15487     else if (element == EL_TIMEGATE_SWITCH ||
15488              element == EL_DC_TIMEGATE_SWITCH)
15489     {
15490       ActivateTimegateSwitch(x, y);
15491     }
15492     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15493              element == EL_BALLOON_SWITCH_RIGHT ||
15494              element == EL_BALLOON_SWITCH_UP    ||
15495              element == EL_BALLOON_SWITCH_DOWN  ||
15496              element == EL_BALLOON_SWITCH_NONE  ||
15497              element == EL_BALLOON_SWITCH_ANY)
15498     {
15499       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15500                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15501                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15502                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15503                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15504                              move_direction);
15505     }
15506     else if (element == EL_LAMP)
15507     {
15508       Feld[x][y] = EL_LAMP_ACTIVE;
15509       local_player->lights_still_needed--;
15510
15511       ResetGfxAnimation(x, y);
15512       TEST_DrawLevelField(x, y);
15513     }
15514     else if (element == EL_TIME_ORB_FULL)
15515     {
15516       Feld[x][y] = EL_TIME_ORB_EMPTY;
15517
15518       if (level.time > 0 || level.use_time_orb_bug)
15519       {
15520         TimeLeft += level.time_orb_time;
15521
15522 #if 1
15523         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15524
15525         DisplayGameControlValues();
15526 #else
15527         DrawGameValue_Time(TimeLeft);
15528 #endif
15529       }
15530
15531       ResetGfxAnimation(x, y);
15532       TEST_DrawLevelField(x, y);
15533     }
15534     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15535              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15536     {
15537       int xx, yy;
15538
15539       game.ball_state = !game.ball_state;
15540
15541       SCAN_PLAYFIELD(xx, yy)
15542       {
15543         int e = Feld[xx][yy];
15544
15545         if (game.ball_state)
15546         {
15547           if (e == EL_EMC_MAGIC_BALL)
15548             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15549           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15550             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15551         }
15552         else
15553         {
15554           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15555             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15556           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15557             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15558         }
15559       }
15560     }
15561
15562     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15563                                         player->index_bit, dig_side);
15564
15565     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15566                                         player->index_bit, dig_side);
15567
15568     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15569                                         player->index_bit, dig_side);
15570
15571     return MP_ACTION;
15572   }
15573   else
15574   {
15575     if (!PLAYER_SWITCHING(player, x, y))
15576     {
15577       player->is_switching = TRUE;
15578       player->switch_x = x;
15579       player->switch_y = y;
15580
15581       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15582                                  player->index_bit, dig_side);
15583       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15584                                           player->index_bit, dig_side);
15585
15586       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15587                                  player->index_bit, dig_side);
15588       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15589                                           player->index_bit, dig_side);
15590     }
15591
15592     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15593                                player->index_bit, dig_side);
15594     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15595                                         player->index_bit, dig_side);
15596
15597     return MP_NO_ACTION;
15598   }
15599
15600   player->push_delay = -1;
15601
15602   if (is_player)                /* function can also be called by EL_PENGUIN */
15603   {
15604     if (Feld[x][y] != element)          /* really digged/collected something */
15605     {
15606       player->is_collecting = !player->is_digging;
15607       player->is_active = TRUE;
15608     }
15609   }
15610
15611   return MP_MOVING;
15612 }
15613
15614 static boolean DigFieldByCE(int x, int y, int digging_element)
15615 {
15616   int element = Feld[x][y];
15617
15618   if (!IS_FREE(x, y))
15619   {
15620     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15621                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15622                   ACTION_BREAKING);
15623
15624     /* no element can dig solid indestructible elements */
15625     if (IS_INDESTRUCTIBLE(element) &&
15626         !IS_DIGGABLE(element) &&
15627         !IS_COLLECTIBLE(element))
15628       return FALSE;
15629
15630     if (AmoebaNr[x][y] &&
15631         (element == EL_AMOEBA_FULL ||
15632          element == EL_BD_AMOEBA ||
15633          element == EL_AMOEBA_GROWING))
15634     {
15635       AmoebaCnt[AmoebaNr[x][y]]--;
15636       AmoebaCnt2[AmoebaNr[x][y]]--;
15637     }
15638
15639     if (IS_MOVING(x, y))
15640       RemoveMovingField(x, y);
15641     else
15642     {
15643       RemoveField(x, y);
15644       TEST_DrawLevelField(x, y);
15645     }
15646
15647     /* if digged element was about to explode, prevent the explosion */
15648     ExplodeField[x][y] = EX_TYPE_NONE;
15649
15650     PlayLevelSoundAction(x, y, action);
15651   }
15652
15653   Store[x][y] = EL_EMPTY;
15654
15655 #if 1
15656   /* this makes it possible to leave the removed element again */
15657   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15658     Store[x][y] = element;
15659 #else
15660   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15661   {
15662     int move_leave_element = element_info[digging_element].move_leave_element;
15663
15664     /* this makes it possible to leave the removed element again */
15665     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15666                    element : move_leave_element);
15667   }
15668 #endif
15669
15670   return TRUE;
15671 }
15672
15673 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15674 {
15675   int jx = player->jx, jy = player->jy;
15676   int x = jx + dx, y = jy + dy;
15677   int snap_direction = (dx == -1 ? MV_LEFT  :
15678                         dx == +1 ? MV_RIGHT :
15679                         dy == -1 ? MV_UP    :
15680                         dy == +1 ? MV_DOWN  : MV_NONE);
15681   boolean can_continue_snapping = (level.continuous_snapping &&
15682                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15683
15684   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15685     return FALSE;
15686
15687   if (!player->active || !IN_LEV_FIELD(x, y))
15688     return FALSE;
15689
15690   if (dx && dy)
15691     return FALSE;
15692
15693   if (!dx && !dy)
15694   {
15695     if (player->MovPos == 0)
15696       player->is_pushing = FALSE;
15697
15698     player->is_snapping = FALSE;
15699
15700     if (player->MovPos == 0)
15701     {
15702       player->is_moving = FALSE;
15703       player->is_digging = FALSE;
15704       player->is_collecting = FALSE;
15705     }
15706
15707     return FALSE;
15708   }
15709
15710 #if USE_NEW_CONTINUOUS_SNAPPING
15711   /* prevent snapping with already pressed snap key when not allowed */
15712   if (player->is_snapping && !can_continue_snapping)
15713     return FALSE;
15714 #else
15715   if (player->is_snapping)
15716     return FALSE;
15717 #endif
15718
15719   player->MovDir = snap_direction;
15720
15721   if (player->MovPos == 0)
15722   {
15723     player->is_moving = FALSE;
15724     player->is_digging = FALSE;
15725     player->is_collecting = FALSE;
15726   }
15727
15728   player->is_dropping = FALSE;
15729   player->is_dropping_pressed = FALSE;
15730   player->drop_pressed_delay = 0;
15731
15732   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15733     return FALSE;
15734
15735   player->is_snapping = TRUE;
15736   player->is_active = TRUE;
15737
15738   if (player->MovPos == 0)
15739   {
15740     player->is_moving = FALSE;
15741     player->is_digging = FALSE;
15742     player->is_collecting = FALSE;
15743   }
15744
15745   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15746     TEST_DrawLevelField(player->last_jx, player->last_jy);
15747
15748   TEST_DrawLevelField(x, y);
15749
15750   return TRUE;
15751 }
15752
15753 static boolean DropElement(struct PlayerInfo *player)
15754 {
15755   int old_element, new_element;
15756   int dropx = player->jx, dropy = player->jy;
15757   int drop_direction = player->MovDir;
15758   int drop_side = drop_direction;
15759 #if 1
15760   int drop_element = get_next_dropped_element(player);
15761 #else
15762   int drop_element = (player->inventory_size > 0 ?
15763                       player->inventory_element[player->inventory_size - 1] :
15764                       player->inventory_infinite_element != EL_UNDEFINED ?
15765                       player->inventory_infinite_element :
15766                       player->dynabombs_left > 0 ?
15767                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15768                       EL_UNDEFINED);
15769 #endif
15770
15771   player->is_dropping_pressed = TRUE;
15772
15773   /* do not drop an element on top of another element; when holding drop key
15774      pressed without moving, dropped element must move away before the next
15775      element can be dropped (this is especially important if the next element
15776      is dynamite, which can be placed on background for historical reasons) */
15777   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15778     return MP_ACTION;
15779
15780   if (IS_THROWABLE(drop_element))
15781   {
15782     dropx += GET_DX_FROM_DIR(drop_direction);
15783     dropy += GET_DY_FROM_DIR(drop_direction);
15784
15785     if (!IN_LEV_FIELD(dropx, dropy))
15786       return FALSE;
15787   }
15788
15789   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15790   new_element = drop_element;           /* default: no change when dropping */
15791
15792   /* check if player is active, not moving and ready to drop */
15793   if (!player->active || player->MovPos || player->drop_delay > 0)
15794     return FALSE;
15795
15796   /* check if player has anything that can be dropped */
15797   if (new_element == EL_UNDEFINED)
15798     return FALSE;
15799
15800   /* check if drop key was pressed long enough for EM style dynamite */
15801   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15802     return FALSE;
15803
15804   /* check if anything can be dropped at the current position */
15805   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15806     return FALSE;
15807
15808   /* collected custom elements can only be dropped on empty fields */
15809   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15810     return FALSE;
15811
15812   if (old_element != EL_EMPTY)
15813     Back[dropx][dropy] = old_element;   /* store old element on this field */
15814
15815   ResetGfxAnimation(dropx, dropy);
15816   ResetRandomAnimationValue(dropx, dropy);
15817
15818   if (player->inventory_size > 0 ||
15819       player->inventory_infinite_element != EL_UNDEFINED)
15820   {
15821     if (player->inventory_size > 0)
15822     {
15823       player->inventory_size--;
15824
15825       DrawGameDoorValues();
15826
15827       if (new_element == EL_DYNAMITE)
15828         new_element = EL_DYNAMITE_ACTIVE;
15829       else if (new_element == EL_EM_DYNAMITE)
15830         new_element = EL_EM_DYNAMITE_ACTIVE;
15831       else if (new_element == EL_SP_DISK_RED)
15832         new_element = EL_SP_DISK_RED_ACTIVE;
15833     }
15834
15835     Feld[dropx][dropy] = new_element;
15836
15837     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15838       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15839                           el2img(Feld[dropx][dropy]), 0);
15840
15841     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15842
15843     /* needed if previous element just changed to "empty" in the last frame */
15844     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15845
15846     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15847                                player->index_bit, drop_side);
15848     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15849                                         CE_PLAYER_DROPS_X,
15850                                         player->index_bit, drop_side);
15851
15852     TestIfElementTouchesCustomElement(dropx, dropy);
15853   }
15854   else          /* player is dropping a dyna bomb */
15855   {
15856     player->dynabombs_left--;
15857
15858     Feld[dropx][dropy] = new_element;
15859
15860     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15861       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15862                           el2img(Feld[dropx][dropy]), 0);
15863
15864     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15865   }
15866
15867   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15868     InitField_WithBug1(dropx, dropy, FALSE);
15869
15870   new_element = Feld[dropx][dropy];     /* element might have changed */
15871
15872   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15873       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15874   {
15875     int move_direction, nextx, nexty;
15876
15877     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15878       MovDir[dropx][dropy] = drop_direction;
15879
15880     move_direction = MovDir[dropx][dropy];
15881     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15882     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15883
15884     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15885
15886 #if USE_FIX_IMPACT_COLLISION
15887     /* do not cause impact style collision by dropping elements that can fall */
15888     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15889 #else
15890     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15891 #endif
15892   }
15893
15894   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15895   player->is_dropping = TRUE;
15896
15897   player->drop_pressed_delay = 0;
15898   player->is_dropping_pressed = FALSE;
15899
15900   player->drop_x = dropx;
15901   player->drop_y = dropy;
15902
15903   return TRUE;
15904 }
15905
15906 /* ------------------------------------------------------------------------- */
15907 /* game sound playing functions                                              */
15908 /* ------------------------------------------------------------------------- */
15909
15910 static int *loop_sound_frame = NULL;
15911 static int *loop_sound_volume = NULL;
15912
15913 void InitPlayLevelSound()
15914 {
15915   int num_sounds = getSoundListSize();
15916
15917   checked_free(loop_sound_frame);
15918   checked_free(loop_sound_volume);
15919
15920   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15921   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15922 }
15923
15924 static void PlayLevelSound(int x, int y, int nr)
15925 {
15926   int sx = SCREENX(x), sy = SCREENY(y);
15927   int volume, stereo_position;
15928   int max_distance = 8;
15929   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15930
15931   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15932       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15933     return;
15934
15935   if (!IN_LEV_FIELD(x, y) ||
15936       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15937       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15938     return;
15939
15940   volume = SOUND_MAX_VOLUME;
15941
15942   if (!IN_SCR_FIELD(sx, sy))
15943   {
15944     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15945     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15946
15947     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15948   }
15949
15950   stereo_position = (SOUND_MAX_LEFT +
15951                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15952                      (SCR_FIELDX + 2 * max_distance));
15953
15954   if (IS_LOOP_SOUND(nr))
15955   {
15956     /* This assures that quieter loop sounds do not overwrite louder ones,
15957        while restarting sound volume comparison with each new game frame. */
15958
15959     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15960       return;
15961
15962     loop_sound_volume[nr] = volume;
15963     loop_sound_frame[nr] = FrameCounter;
15964   }
15965
15966   PlaySoundExt(nr, volume, stereo_position, type);
15967 }
15968
15969 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15970 {
15971   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15972                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15973                  y < LEVELY(BY1) ? LEVELY(BY1) :
15974                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15975                  sound_action);
15976 }
15977
15978 static void PlayLevelSoundAction(int x, int y, int action)
15979 {
15980   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15981 }
15982
15983 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15984 {
15985   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15986
15987   if (sound_effect != SND_UNDEFINED)
15988     PlayLevelSound(x, y, sound_effect);
15989 }
15990
15991 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15992                                               int action)
15993 {
15994   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15995
15996   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15997     PlayLevelSound(x, y, sound_effect);
15998 }
15999
16000 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16001 {
16002   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16003
16004   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16005     PlayLevelSound(x, y, sound_effect);
16006 }
16007
16008 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16009 {
16010   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16011
16012   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16013     StopSound(sound_effect);
16014 }
16015
16016 static void PlayLevelMusic()
16017 {
16018   if (levelset.music[level_nr] != MUS_UNDEFINED)
16019     PlayMusic(levelset.music[level_nr]);        /* from config file */
16020   else
16021     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16022 }
16023
16024 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16025 {
16026   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16027   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16028   int x = xx - 1 - offset;
16029   int y = yy - 1 - offset;
16030
16031   switch (sample)
16032   {
16033     case SAMPLE_blank:
16034       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16035       break;
16036
16037     case SAMPLE_roll:
16038       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16039       break;
16040
16041     case SAMPLE_stone:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16043       break;
16044
16045     case SAMPLE_nut:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16047       break;
16048
16049     case SAMPLE_crack:
16050       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16051       break;
16052
16053     case SAMPLE_bug:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16055       break;
16056
16057     case SAMPLE_tank:
16058       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16059       break;
16060
16061     case SAMPLE_android_clone:
16062       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16063       break;
16064
16065     case SAMPLE_android_move:
16066       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16067       break;
16068
16069     case SAMPLE_spring:
16070       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16071       break;
16072
16073     case SAMPLE_slurp:
16074       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16075       break;
16076
16077     case SAMPLE_eater:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16079       break;
16080
16081     case SAMPLE_eater_eat:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16083       break;
16084
16085     case SAMPLE_alien:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16087       break;
16088
16089     case SAMPLE_collect:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16091       break;
16092
16093     case SAMPLE_diamond:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16095       break;
16096
16097     case SAMPLE_squash:
16098       /* !!! CHECK THIS !!! */
16099 #if 1
16100       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16101 #else
16102       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16103 #endif
16104       break;
16105
16106     case SAMPLE_wonderfall:
16107       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16108       break;
16109
16110     case SAMPLE_drip:
16111       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16112       break;
16113
16114     case SAMPLE_push:
16115       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16116       break;
16117
16118     case SAMPLE_dirt:
16119       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16120       break;
16121
16122     case SAMPLE_acid:
16123       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16124       break;
16125
16126     case SAMPLE_ball:
16127       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16128       break;
16129
16130     case SAMPLE_grow:
16131       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16132       break;
16133
16134     case SAMPLE_wonder:
16135       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16136       break;
16137
16138     case SAMPLE_door:
16139       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16140       break;
16141
16142     case SAMPLE_exit_open:
16143       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16144       break;
16145
16146     case SAMPLE_exit_leave:
16147       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16148       break;
16149
16150     case SAMPLE_dynamite:
16151       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16152       break;
16153
16154     case SAMPLE_tick:
16155       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16156       break;
16157
16158     case SAMPLE_press:
16159       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16160       break;
16161
16162     case SAMPLE_wheel:
16163       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16164       break;
16165
16166     case SAMPLE_boom:
16167       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16168       break;
16169
16170     case SAMPLE_die:
16171       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16172       break;
16173
16174     case SAMPLE_time:
16175       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16176       break;
16177
16178     default:
16179       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16180       break;
16181   }
16182 }
16183
16184 #if 0
16185 void ChangeTime(int value)
16186 {
16187   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16188
16189   *time += value;
16190
16191   /* EMC game engine uses value from time counter of RND game engine */
16192   level.native_em_level->lev->time = *time;
16193
16194   DrawGameValue_Time(*time);
16195 }
16196
16197 void RaiseScore(int value)
16198 {
16199   /* EMC game engine and RND game engine have separate score counters */
16200   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16201                 &level.native_em_level->lev->score : &local_player->score);
16202
16203   *score += value;
16204
16205   DrawGameValue_Score(*score);
16206 }
16207 #endif
16208
16209 void RaiseScore(int value)
16210 {
16211   local_player->score += value;
16212
16213 #if 1
16214   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16215
16216   DisplayGameControlValues();
16217 #else
16218   DrawGameValue_Score(local_player->score);
16219 #endif
16220 }
16221
16222 void RaiseScoreElement(int element)
16223 {
16224   switch (element)
16225   {
16226     case EL_EMERALD:
16227     case EL_BD_DIAMOND:
16228     case EL_EMERALD_YELLOW:
16229     case EL_EMERALD_RED:
16230     case EL_EMERALD_PURPLE:
16231     case EL_SP_INFOTRON:
16232       RaiseScore(level.score[SC_EMERALD]);
16233       break;
16234     case EL_DIAMOND:
16235       RaiseScore(level.score[SC_DIAMOND]);
16236       break;
16237     case EL_CRYSTAL:
16238       RaiseScore(level.score[SC_CRYSTAL]);
16239       break;
16240     case EL_PEARL:
16241       RaiseScore(level.score[SC_PEARL]);
16242       break;
16243     case EL_BUG:
16244     case EL_BD_BUTTERFLY:
16245     case EL_SP_ELECTRON:
16246       RaiseScore(level.score[SC_BUG]);
16247       break;
16248     case EL_SPACESHIP:
16249     case EL_BD_FIREFLY:
16250     case EL_SP_SNIKSNAK:
16251       RaiseScore(level.score[SC_SPACESHIP]);
16252       break;
16253     case EL_YAMYAM:
16254     case EL_DARK_YAMYAM:
16255       RaiseScore(level.score[SC_YAMYAM]);
16256       break;
16257     case EL_ROBOT:
16258       RaiseScore(level.score[SC_ROBOT]);
16259       break;
16260     case EL_PACMAN:
16261       RaiseScore(level.score[SC_PACMAN]);
16262       break;
16263     case EL_NUT:
16264       RaiseScore(level.score[SC_NUT]);
16265       break;
16266     case EL_DYNAMITE:
16267     case EL_EM_DYNAMITE:
16268     case EL_SP_DISK_RED:
16269     case EL_DYNABOMB_INCREASE_NUMBER:
16270     case EL_DYNABOMB_INCREASE_SIZE:
16271     case EL_DYNABOMB_INCREASE_POWER:
16272       RaiseScore(level.score[SC_DYNAMITE]);
16273       break;
16274     case EL_SHIELD_NORMAL:
16275     case EL_SHIELD_DEADLY:
16276       RaiseScore(level.score[SC_SHIELD]);
16277       break;
16278     case EL_EXTRA_TIME:
16279       RaiseScore(level.extra_time_score);
16280       break;
16281     case EL_KEY_1:
16282     case EL_KEY_2:
16283     case EL_KEY_3:
16284     case EL_KEY_4:
16285     case EL_EM_KEY_1:
16286     case EL_EM_KEY_2:
16287     case EL_EM_KEY_3:
16288     case EL_EM_KEY_4:
16289     case EL_EMC_KEY_5:
16290     case EL_EMC_KEY_6:
16291     case EL_EMC_KEY_7:
16292     case EL_EMC_KEY_8:
16293     case EL_DC_KEY_WHITE:
16294       RaiseScore(level.score[SC_KEY]);
16295       break;
16296     default:
16297       RaiseScore(element_info[element].collect_score);
16298       break;
16299   }
16300 }
16301
16302 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16303 {
16304   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16305   {
16306 #if defined(NETWORK_AVALIABLE)
16307     if (options.network)
16308       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16309     else
16310 #endif
16311     {
16312       if (quick_quit)
16313       {
16314 #if 1
16315
16316 #if 1
16317         FadeSkipNextFadeIn();
16318 #else
16319         fading = fading_none;
16320 #endif
16321
16322 #else
16323         OpenDoor(DOOR_CLOSE_1);
16324 #endif
16325
16326         game_status = GAME_MODE_MAIN;
16327
16328 #if 1
16329         DrawAndFadeInMainMenu(REDRAW_FIELD);
16330 #else
16331         DrawMainMenu();
16332 #endif
16333       }
16334       else
16335       {
16336 #if 0
16337         FadeOut(REDRAW_FIELD);
16338 #endif
16339
16340         game_status = GAME_MODE_MAIN;
16341
16342         DrawAndFadeInMainMenu(REDRAW_FIELD);
16343       }
16344     }
16345   }
16346   else          /* continue playing the game */
16347   {
16348     if (tape.playing && tape.deactivate_display)
16349       TapeDeactivateDisplayOff(TRUE);
16350
16351     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16352
16353     if (tape.playing && tape.deactivate_display)
16354       TapeDeactivateDisplayOn();
16355   }
16356 }
16357
16358 void RequestQuitGame(boolean ask_if_really_quit)
16359 {
16360   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16361   boolean skip_request = AllPlayersGone || quick_quit;
16362
16363   RequestQuitGameExt(skip_request, quick_quit,
16364                      "Do you really want to quit the game ?");
16365 }
16366
16367
16368 /* ------------------------------------------------------------------------- */
16369 /* random generator functions                                                */
16370 /* ------------------------------------------------------------------------- */
16371
16372 unsigned int InitEngineRandom_RND(long seed)
16373 {
16374   game.num_random_calls = 0;
16375
16376 #if 0
16377   unsigned int rnd_seed = InitEngineRandom(seed);
16378
16379   printf("::: START RND: %d\n", rnd_seed);
16380
16381   return rnd_seed;
16382 #else
16383
16384   return InitEngineRandom(seed);
16385
16386 #endif
16387
16388 }
16389
16390 unsigned int RND(int max)
16391 {
16392   if (max > 0)
16393   {
16394     game.num_random_calls++;
16395
16396     return GetEngineRandom(max);
16397   }
16398
16399   return 0;
16400 }
16401
16402
16403 /* ------------------------------------------------------------------------- */
16404 /* game engine snapshot handling functions                                   */
16405 /* ------------------------------------------------------------------------- */
16406
16407 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16408
16409 struct EngineSnapshotInfo
16410 {
16411   /* runtime values for custom element collect score */
16412   int collect_score[NUM_CUSTOM_ELEMENTS];
16413
16414   /* runtime values for group element choice position */
16415   int choice_pos[NUM_GROUP_ELEMENTS];
16416
16417   /* runtime values for belt position animations */
16418   int belt_graphic[4 * NUM_BELT_PARTS];
16419   int belt_anim_mode[4 * NUM_BELT_PARTS];
16420 };
16421
16422 struct EngineSnapshotNodeInfo
16423 {
16424   void *buffer_orig;
16425   void *buffer_copy;
16426   int size;
16427 };
16428
16429 static struct EngineSnapshotInfo engine_snapshot_rnd;
16430 static ListNode *engine_snapshot_list = NULL;
16431 static char *snapshot_level_identifier = NULL;
16432 static int snapshot_level_nr = -1;
16433
16434 void FreeEngineSnapshot()
16435 {
16436   while (engine_snapshot_list != NULL)
16437     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16438                        checked_free);
16439
16440   setString(&snapshot_level_identifier, NULL);
16441   snapshot_level_nr = -1;
16442 }
16443
16444 static void SaveEngineSnapshotValues_RND()
16445 {
16446   static int belt_base_active_element[4] =
16447   {
16448     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16449     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16450     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16451     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16452   };
16453   int i, j;
16454
16455   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16456   {
16457     int element = EL_CUSTOM_START + i;
16458
16459     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16460   }
16461
16462   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16463   {
16464     int element = EL_GROUP_START + i;
16465
16466     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16467   }
16468
16469   for (i = 0; i < 4; i++)
16470   {
16471     for (j = 0; j < NUM_BELT_PARTS; j++)
16472     {
16473       int element = belt_base_active_element[i] + j;
16474       int graphic = el2img(element);
16475       int anim_mode = graphic_info[graphic].anim_mode;
16476
16477       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16478       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16479     }
16480   }
16481 }
16482
16483 static void LoadEngineSnapshotValues_RND()
16484 {
16485   unsigned long num_random_calls = game.num_random_calls;
16486   int i, j;
16487
16488   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16489   {
16490     int element = EL_CUSTOM_START + i;
16491
16492     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16493   }
16494
16495   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16496   {
16497     int element = EL_GROUP_START + i;
16498
16499     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16500   }
16501
16502   for (i = 0; i < 4; i++)
16503   {
16504     for (j = 0; j < NUM_BELT_PARTS; j++)
16505     {
16506       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16507       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16508
16509       graphic_info[graphic].anim_mode = anim_mode;
16510     }
16511   }
16512
16513   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16514   {
16515     InitRND(tape.random_seed);
16516     for (i = 0; i < num_random_calls; i++)
16517       RND(1);
16518   }
16519
16520   if (game.num_random_calls != num_random_calls)
16521   {
16522     Error(ERR_INFO, "number of random calls out of sync");
16523     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16524     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16525     Error(ERR_EXIT, "this should not happen -- please debug");
16526   }
16527 }
16528
16529 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16530 {
16531   struct EngineSnapshotNodeInfo *bi =
16532     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16533
16534   bi->buffer_orig = buffer;
16535   bi->buffer_copy = checked_malloc(size);
16536   bi->size = size;
16537
16538   memcpy(bi->buffer_copy, buffer, size);
16539
16540   addNodeToList(&engine_snapshot_list, NULL, bi);
16541 }
16542
16543 void SaveEngineSnapshot()
16544 {
16545   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16546
16547   if (level_editor_test_game)   /* do not save snapshots from editor */
16548     return;
16549
16550   /* copy some special values to a structure better suited for the snapshot */
16551
16552   SaveEngineSnapshotValues_RND();
16553   SaveEngineSnapshotValues_EM();
16554
16555   /* save values stored in special snapshot structure */
16556
16557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16559
16560   /* save further RND engine values */
16561
16562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16565
16566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16570
16571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16576
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16580
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16582
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16584
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16587
16588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16606
16607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16609
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16613
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16616
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16622
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16625
16626   /* save level identification information */
16627
16628   setString(&snapshot_level_identifier, leveldir_current->identifier);
16629   snapshot_level_nr = level_nr;
16630
16631 #if 0
16632   ListNode *node = engine_snapshot_list;
16633   int num_bytes = 0;
16634
16635   while (node != NULL)
16636   {
16637     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16638
16639     node = node->next;
16640   }
16641
16642   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16643 #endif
16644 }
16645
16646 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16647 {
16648   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16649 }
16650
16651 void LoadEngineSnapshot()
16652 {
16653   ListNode *node = engine_snapshot_list;
16654
16655   if (engine_snapshot_list == NULL)
16656     return;
16657
16658   while (node != NULL)
16659   {
16660     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16661
16662     node = node->next;
16663   }
16664
16665   /* restore special values from snapshot structure */
16666
16667   LoadEngineSnapshotValues_RND();
16668   LoadEngineSnapshotValues_EM();
16669 }
16670
16671 boolean CheckEngineSnapshot()
16672 {
16673   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16674           snapshot_level_nr == level_nr);
16675 }
16676
16677
16678 /* ---------- new game button stuff ---------------------------------------- */
16679
16680 /* graphic position values for game buttons */
16681 #define GAME_BUTTON_XSIZE       30
16682 #define GAME_BUTTON_YSIZE       30
16683 #define GAME_BUTTON_XPOS        5
16684 #define GAME_BUTTON_YPOS        215
16685 #define SOUND_BUTTON_XPOS       5
16686 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16687
16688 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16689 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16690 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16691 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16692 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16693 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16694
16695 static struct
16696 {
16697   int *x, *y;
16698   int gd_x, gd_y;
16699   int gadget_id;
16700   char *infotext;
16701 } gamebutton_info[NUM_GAME_BUTTONS] =
16702 {
16703 #if 1
16704   {
16705     &game.button.stop.x,        &game.button.stop.y,
16706     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16707     GAME_CTRL_ID_STOP,
16708     "stop game"
16709   },
16710   {
16711     &game.button.pause.x,       &game.button.pause.y,
16712     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16713     GAME_CTRL_ID_PAUSE,
16714     "pause game"
16715   },
16716   {
16717     &game.button.play.x,        &game.button.play.y,
16718     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16719     GAME_CTRL_ID_PLAY,
16720     "play game"
16721   },
16722   {
16723     &game.button.sound_music.x, &game.button.sound_music.y,
16724     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16725     SOUND_CTRL_ID_MUSIC,
16726     "background music on/off"
16727   },
16728   {
16729     &game.button.sound_loops.x, &game.button.sound_loops.y,
16730     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16731     SOUND_CTRL_ID_LOOPS,
16732     "sound loops on/off"
16733   },
16734   {
16735     &game.button.sound_simple.x,&game.button.sound_simple.y,
16736     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16737     SOUND_CTRL_ID_SIMPLE,
16738     "normal sounds on/off"
16739   }
16740 #else
16741   {
16742     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16743     GAME_CTRL_ID_STOP,
16744     "stop game"
16745   },
16746   {
16747     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16748     GAME_CTRL_ID_PAUSE,
16749     "pause game"
16750   },
16751   {
16752     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16753     GAME_CTRL_ID_PLAY,
16754     "play game"
16755   },
16756   {
16757     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16758     SOUND_CTRL_ID_MUSIC,
16759     "background music on/off"
16760   },
16761   {
16762     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16763     SOUND_CTRL_ID_LOOPS,
16764     "sound loops on/off"
16765   },
16766   {
16767     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16768     SOUND_CTRL_ID_SIMPLE,
16769     "normal sounds on/off"
16770   }
16771 #endif
16772 };
16773
16774 void CreateGameButtons()
16775 {
16776   int i;
16777
16778   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16779   {
16780     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16781     struct GadgetInfo *gi;
16782     int button_type;
16783     boolean checked;
16784     unsigned long event_mask;
16785     int x, y;
16786     int gd_xoffset, gd_yoffset;
16787     int gd_x1, gd_x2, gd_y1, gd_y2;
16788     int id = i;
16789
16790     x = DX + *gamebutton_info[i].x;
16791     y = DY + *gamebutton_info[i].y;
16792     gd_xoffset = gamebutton_info[i].gd_x;
16793     gd_yoffset = gamebutton_info[i].gd_y;
16794     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16795     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16796
16797     if (id == GAME_CTRL_ID_STOP ||
16798         id == GAME_CTRL_ID_PAUSE ||
16799         id == GAME_CTRL_ID_PLAY)
16800     {
16801       button_type = GD_TYPE_NORMAL_BUTTON;
16802       checked = FALSE;
16803       event_mask = GD_EVENT_RELEASED;
16804       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16805       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16806     }
16807     else
16808     {
16809       button_type = GD_TYPE_CHECK_BUTTON;
16810       checked =
16811         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16812          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16813          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16814       event_mask = GD_EVENT_PRESSED;
16815       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16816       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16817     }
16818
16819     gi = CreateGadget(GDI_CUSTOM_ID, id,
16820                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16821 #if 1
16822                       GDI_X, x,
16823                       GDI_Y, y,
16824 #else
16825                       GDI_X, DX + gd_xoffset,
16826                       GDI_Y, DY + gd_yoffset,
16827 #endif
16828                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16829                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16830                       GDI_TYPE, button_type,
16831                       GDI_STATE, GD_BUTTON_UNPRESSED,
16832                       GDI_CHECKED, checked,
16833                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16834                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16835                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16836                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16837                       GDI_DIRECT_DRAW, FALSE,
16838                       GDI_EVENT_MASK, event_mask,
16839                       GDI_CALLBACK_ACTION, HandleGameButtons,
16840                       GDI_END);
16841
16842     if (gi == NULL)
16843       Error(ERR_EXIT, "cannot create gadget");
16844
16845     game_gadget[id] = gi;
16846   }
16847 }
16848
16849 void FreeGameButtons()
16850 {
16851   int i;
16852
16853   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16854     FreeGadget(game_gadget[i]);
16855 }
16856
16857 static void MapGameButtons()
16858 {
16859   int i;
16860
16861   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862     MapGadget(game_gadget[i]);
16863 }
16864
16865 void UnmapGameButtons()
16866 {
16867   int i;
16868
16869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870     UnmapGadget(game_gadget[i]);
16871 }
16872
16873 void RedrawGameButtons()
16874 {
16875   int i;
16876
16877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878     RedrawGadget(game_gadget[i]);
16879 }
16880
16881 static void HandleGameButtons(struct GadgetInfo *gi)
16882 {
16883   int id = gi->custom_id;
16884
16885   if (game_status != GAME_MODE_PLAYING)
16886     return;
16887
16888   switch (id)
16889   {
16890     case GAME_CTRL_ID_STOP:
16891       if (tape.playing)
16892         TapeStop();
16893       else
16894         RequestQuitGame(TRUE);
16895       break;
16896
16897     case GAME_CTRL_ID_PAUSE:
16898       if (options.network)
16899       {
16900 #if defined(NETWORK_AVALIABLE)
16901         if (tape.pausing)
16902           SendToServer_ContinuePlaying();
16903         else
16904           SendToServer_PausePlaying();
16905 #endif
16906       }
16907       else
16908         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16909       break;
16910
16911     case GAME_CTRL_ID_PLAY:
16912       if (tape.pausing)
16913       {
16914 #if defined(NETWORK_AVALIABLE)
16915         if (options.network)
16916           SendToServer_ContinuePlaying();
16917         else
16918 #endif
16919         {
16920           tape.pausing = FALSE;
16921           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16922         }
16923       }
16924       break;
16925
16926     case SOUND_CTRL_ID_MUSIC:
16927       if (setup.sound_music)
16928       { 
16929         setup.sound_music = FALSE;
16930         FadeMusic();
16931       }
16932       else if (audio.music_available)
16933       { 
16934         setup.sound = setup.sound_music = TRUE;
16935
16936         SetAudioMode(setup.sound);
16937
16938         PlayLevelMusic();
16939       }
16940       break;
16941
16942     case SOUND_CTRL_ID_LOOPS:
16943       if (setup.sound_loops)
16944         setup.sound_loops = FALSE;
16945       else if (audio.loops_available)
16946       {
16947         setup.sound = setup.sound_loops = TRUE;
16948         SetAudioMode(setup.sound);
16949       }
16950       break;
16951
16952     case SOUND_CTRL_ID_SIMPLE:
16953       if (setup.sound_simple)
16954         setup.sound_simple = FALSE;
16955       else if (audio.sound_available)
16956       {
16957         setup.sound = setup.sound_simple = TRUE;
16958         SetAudioMode(setup.sound);
16959       }
16960       break;
16961
16962     default:
16963       break;
16964   }
16965 }