80f157012eddb293b42049a8db6e7ee26976d99d
[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 (button1 || (dropped && !moved))
11987       {
11988         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11989         SnapField(player, 0, 0);                /* stop snapping */
11990       }
11991     }
11992
11993     SetPlayerWaiting(player, FALSE);
11994
11995     return player_action;
11996   }
11997   else
11998   {
11999     /* no actions for this player (no input at player's configured device) */
12000
12001     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12002     SnapField(player, 0, 0);
12003     CheckGravityMovementWhenNotMoving(player);
12004
12005     if (player->MovPos == 0)
12006       SetPlayerWaiting(player, TRUE);
12007
12008     if (player->MovPos == 0)    /* needed for tape.playing */
12009       player->is_moving = FALSE;
12010
12011     player->is_dropping = FALSE;
12012     player->is_dropping_pressed = FALSE;
12013     player->drop_pressed_delay = 0;
12014
12015     return 0;
12016   }
12017 }
12018
12019 static void CheckLevelTime()
12020 {
12021   int i;
12022
12023   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12024   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12025   {
12026     if (level.native_em_level->lev->home == 0)  /* all players at home */
12027     {
12028       PlayerWins(local_player);
12029
12030       AllPlayersGone = TRUE;
12031
12032       level.native_em_level->lev->home = -1;
12033     }
12034
12035     if (level.native_em_level->ply[0]->alive == 0 &&
12036         level.native_em_level->ply[1]->alive == 0 &&
12037         level.native_em_level->ply[2]->alive == 0 &&
12038         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12039       AllPlayersGone = TRUE;
12040   }
12041   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12042   {
12043     if (game_sp_info.LevelSolved &&
12044         !game_sp_info.GameOver)                         /* game won */
12045     {
12046       PlayerWins(local_player);
12047
12048       game_sp_info.GameOver = TRUE;
12049
12050       AllPlayersGone = TRUE;
12051     }
12052
12053     if (game_sp_info.GameOver)                          /* game lost */
12054       AllPlayersGone = TRUE;
12055   }
12056
12057   if (TimeFrames >= FRAMES_PER_SECOND)
12058   {
12059     TimeFrames = 0;
12060     TapeTime++;
12061
12062     for (i = 0; i < MAX_PLAYERS; i++)
12063     {
12064       struct PlayerInfo *player = &stored_player[i];
12065
12066       if (SHIELD_ON(player))
12067       {
12068         player->shield_normal_time_left--;
12069
12070         if (player->shield_deadly_time_left > 0)
12071           player->shield_deadly_time_left--;
12072       }
12073     }
12074
12075     if (!local_player->LevelSolved && !level.use_step_counter)
12076     {
12077       TimePlayed++;
12078
12079       if (TimeLeft > 0)
12080       {
12081         TimeLeft--;
12082
12083         if (TimeLeft <= 10 && setup.time_limit)
12084           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12085
12086 #if 1
12087         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12088
12089         DisplayGameControlValues();
12090 #else
12091         DrawGameValue_Time(TimeLeft);
12092 #endif
12093
12094         if (!TimeLeft && setup.time_limit)
12095         {
12096           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12097             level.native_em_level->lev->killed_out_of_time = TRUE;
12098           else
12099             for (i = 0; i < MAX_PLAYERS; i++)
12100               KillPlayer(&stored_player[i]);
12101         }
12102       }
12103 #if 1
12104       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12105       {
12106         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12107
12108         DisplayGameControlValues();
12109       }
12110 #else
12111       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12112         DrawGameValue_Time(TimePlayed);
12113 #endif
12114
12115       level.native_em_level->lev->time =
12116         (level.time == 0 ? TimePlayed : TimeLeft);
12117     }
12118
12119     if (tape.recording || tape.playing)
12120       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12121   }
12122
12123 #if 1
12124   UpdateAndDisplayGameControlValues();
12125 #else
12126   UpdateGameDoorValues();
12127   DrawGameDoorValues();
12128 #endif
12129 }
12130
12131 void AdvanceFrameAndPlayerCounters(int player_nr)
12132 {
12133   int i;
12134
12135   /* advance frame counters (global frame counter and time frame counter) */
12136   FrameCounter++;
12137   TimeFrames++;
12138
12139   /* advance player counters (counters for move delay, move animation etc.) */
12140   for (i = 0; i < MAX_PLAYERS; i++)
12141   {
12142     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12143     int move_delay_value = stored_player[i].move_delay_value;
12144     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12145
12146     if (!advance_player_counters)       /* not all players may be affected */
12147       continue;
12148
12149 #if USE_NEW_PLAYER_ANIM
12150     if (move_frames == 0)       /* less than one move per game frame */
12151     {
12152       int stepsize = TILEX / move_delay_value;
12153       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12154       int count = (stored_player[i].is_moving ?
12155                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12156
12157       if (count % delay == 0)
12158         move_frames = 1;
12159     }
12160 #endif
12161
12162     stored_player[i].Frame += move_frames;
12163
12164     if (stored_player[i].MovPos != 0)
12165       stored_player[i].StepFrame += move_frames;
12166
12167     if (stored_player[i].move_delay > 0)
12168       stored_player[i].move_delay--;
12169
12170     /* due to bugs in previous versions, counter must count up, not down */
12171     if (stored_player[i].push_delay != -1)
12172       stored_player[i].push_delay++;
12173
12174     if (stored_player[i].drop_delay > 0)
12175       stored_player[i].drop_delay--;
12176
12177     if (stored_player[i].is_dropping_pressed)
12178       stored_player[i].drop_pressed_delay++;
12179   }
12180 }
12181
12182 void StartGameActions(boolean init_network_game, boolean record_tape,
12183                       long random_seed)
12184 {
12185   unsigned long new_random_seed = InitRND(random_seed);
12186
12187   if (record_tape)
12188     TapeStartRecording(new_random_seed);
12189
12190 #if defined(NETWORK_AVALIABLE)
12191   if (init_network_game)
12192   {
12193     SendToServer_StartPlaying();
12194
12195     return;
12196   }
12197 #endif
12198
12199   InitGame();
12200 }
12201
12202 void GameActions()
12203 {
12204   static unsigned long game_frame_delay = 0;
12205   unsigned long game_frame_delay_value;
12206   byte *recorded_player_action;
12207   byte summarized_player_action = 0;
12208   byte tape_action[MAX_PLAYERS];
12209   int i;
12210
12211   /* detect endless loops, caused by custom element programming */
12212   if (recursion_loop_detected && recursion_loop_depth == 0)
12213   {
12214     char *message = getStringCat3("Internal Error ! Element ",
12215                                   EL_NAME(recursion_loop_element),
12216                                   " caused endless loop ! Quit the game ?");
12217
12218     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12219           EL_NAME(recursion_loop_element));
12220
12221     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12222
12223     recursion_loop_detected = FALSE;    /* if game should be continued */
12224
12225     free(message);
12226
12227     return;
12228   }
12229
12230   if (game.restart_level)
12231     StartGameActions(options.network, setup.autorecord, level.random_seed);
12232
12233   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12234   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12235   {
12236     if (level.native_em_level->lev->home == 0)  /* all players at home */
12237     {
12238       PlayerWins(local_player);
12239
12240       AllPlayersGone = TRUE;
12241
12242       level.native_em_level->lev->home = -1;
12243     }
12244
12245     if (level.native_em_level->ply[0]->alive == 0 &&
12246         level.native_em_level->ply[1]->alive == 0 &&
12247         level.native_em_level->ply[2]->alive == 0 &&
12248         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12249       AllPlayersGone = TRUE;
12250   }
12251   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12252   {
12253     if (game_sp_info.LevelSolved &&
12254         !game_sp_info.GameOver)                         /* game won */
12255     {
12256       PlayerWins(local_player);
12257
12258       game_sp_info.GameOver = TRUE;
12259
12260       AllPlayersGone = TRUE;
12261     }
12262
12263     if (game_sp_info.GameOver)                          /* game lost */
12264       AllPlayersGone = TRUE;
12265   }
12266
12267   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12268     GameWon();
12269
12270   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12271     TapeStop();
12272
12273   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12274     return;
12275
12276   game_frame_delay_value =
12277     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12278
12279   if (tape.playing && tape.warp_forward && !tape.pausing)
12280     game_frame_delay_value = 0;
12281
12282   /* ---------- main game synchronization point ---------- */
12283
12284   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12285
12286   if (network_playing && !network_player_action_received)
12287   {
12288     /* try to get network player actions in time */
12289
12290 #if defined(NETWORK_AVALIABLE)
12291     /* last chance to get network player actions without main loop delay */
12292     HandleNetworking();
12293 #endif
12294
12295     /* game was quit by network peer */
12296     if (game_status != GAME_MODE_PLAYING)
12297       return;
12298
12299     if (!network_player_action_received)
12300       return;           /* failed to get network player actions in time */
12301
12302     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12303   }
12304
12305   if (tape.pausing)
12306     return;
12307
12308   /* at this point we know that we really continue executing the game */
12309
12310   network_player_action_received = FALSE;
12311
12312   /* when playing tape, read previously recorded player input from tape data */
12313   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12314
12315 #if 1
12316   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12317   if (tape.pausing)
12318     return;
12319 #endif
12320
12321   if (tape.set_centered_player)
12322   {
12323     game.centered_player_nr_next = tape.centered_player_nr_next;
12324     game.set_centered_player = TRUE;
12325   }
12326
12327   for (i = 0; i < MAX_PLAYERS; i++)
12328   {
12329     summarized_player_action |= stored_player[i].action;
12330
12331     if (!network_playing)
12332       stored_player[i].effective_action = stored_player[i].action;
12333   }
12334
12335 #if defined(NETWORK_AVALIABLE)
12336   if (network_playing)
12337     SendToServer_MovePlayer(summarized_player_action);
12338 #endif
12339
12340   if (!options.network && !setup.team_mode)
12341     local_player->effective_action = summarized_player_action;
12342
12343   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12344   {
12345     for (i = 0; i < MAX_PLAYERS; i++)
12346       stored_player[i].effective_action =
12347         (i == game.centered_player_nr ? summarized_player_action : 0);
12348   }
12349
12350   if (recorded_player_action != NULL)
12351     for (i = 0; i < MAX_PLAYERS; i++)
12352       stored_player[i].effective_action = recorded_player_action[i];
12353
12354   for (i = 0; i < MAX_PLAYERS; i++)
12355   {
12356     tape_action[i] = stored_player[i].effective_action;
12357
12358     /* (this can only happen in the R'n'D game engine) */
12359     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12360       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12361   }
12362
12363   /* only record actions from input devices, but not programmed actions */
12364   if (tape.recording)
12365     TapeRecordAction(tape_action);
12366
12367 #if USE_NEW_PLAYER_ASSIGNMENTS
12368   {
12369     byte mapped_action[MAX_PLAYERS];
12370
12371     for (i = 0; i < MAX_PLAYERS; i++)
12372       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12373
12374     for (i = 0; i < MAX_PLAYERS; i++)
12375       stored_player[i].effective_action = mapped_action[i];
12376   }
12377 #endif
12378
12379   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12380   {
12381     GameActions_EM_Main();
12382   }
12383   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12384   {
12385     GameActions_SP_Main();
12386   }
12387   else
12388   {
12389     GameActions_RND();
12390   }
12391 }
12392
12393 void GameActions_EM_Main()
12394 {
12395   byte effective_action[MAX_PLAYERS];
12396   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12397   int i;
12398
12399   for (i = 0; i < MAX_PLAYERS; i++)
12400     effective_action[i] = stored_player[i].effective_action;
12401
12402   GameActions_EM(effective_action, warp_mode);
12403
12404   CheckLevelTime();
12405
12406   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12407 }
12408
12409 void GameActions_SP_Main()
12410 {
12411   byte effective_action[MAX_PLAYERS];
12412   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12413   int i;
12414
12415   for (i = 0; i < MAX_PLAYERS; i++)
12416     effective_action[i] = stored_player[i].effective_action;
12417
12418   GameActions_SP(effective_action, warp_mode);
12419
12420   CheckLevelTime();
12421
12422   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12423 }
12424
12425 void GameActions_RND()
12426 {
12427   int magic_wall_x = 0, magic_wall_y = 0;
12428   int i, x, y, element, graphic;
12429
12430   InitPlayfieldScanModeVars();
12431
12432 #if USE_ONE_MORE_CHANGE_PER_FRAME
12433   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12434   {
12435     SCAN_PLAYFIELD(x, y)
12436     {
12437       ChangeCount[x][y] = 0;
12438       ChangeEvent[x][y] = -1;
12439     }
12440   }
12441 #endif
12442
12443   if (game.set_centered_player)
12444   {
12445     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12446
12447     /* switching to "all players" only possible if all players fit to screen */
12448     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12449     {
12450       game.centered_player_nr_next = game.centered_player_nr;
12451       game.set_centered_player = FALSE;
12452     }
12453
12454     /* do not switch focus to non-existing (or non-active) player */
12455     if (game.centered_player_nr_next >= 0 &&
12456         !stored_player[game.centered_player_nr_next].active)
12457     {
12458       game.centered_player_nr_next = game.centered_player_nr;
12459       game.set_centered_player = FALSE;
12460     }
12461   }
12462
12463   if (game.set_centered_player &&
12464       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12465   {
12466     int sx, sy;
12467
12468     if (game.centered_player_nr_next == -1)
12469     {
12470       setScreenCenteredToAllPlayers(&sx, &sy);
12471     }
12472     else
12473     {
12474       sx = stored_player[game.centered_player_nr_next].jx;
12475       sy = stored_player[game.centered_player_nr_next].jy;
12476     }
12477
12478     game.centered_player_nr = game.centered_player_nr_next;
12479     game.set_centered_player = FALSE;
12480
12481     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12482     DrawGameDoorValues();
12483   }
12484
12485   for (i = 0; i < MAX_PLAYERS; i++)
12486   {
12487     int actual_player_action = stored_player[i].effective_action;
12488
12489 #if 1
12490     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12491        - rnd_equinox_tetrachloride 048
12492        - rnd_equinox_tetrachloride_ii 096
12493        - rnd_emanuel_schmieg 002
12494        - doctor_sloan_ww 001, 020
12495     */
12496     if (stored_player[i].MovPos == 0)
12497       CheckGravityMovement(&stored_player[i]);
12498 #endif
12499
12500     /* overwrite programmed action with tape action */
12501     if (stored_player[i].programmed_action)
12502       actual_player_action = stored_player[i].programmed_action;
12503
12504     PlayerActions(&stored_player[i], actual_player_action);
12505
12506     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12507   }
12508
12509   ScrollScreen(NULL, SCROLL_GO_ON);
12510
12511   /* for backwards compatibility, the following code emulates a fixed bug that
12512      occured when pushing elements (causing elements that just made their last
12513      pushing step to already (if possible) make their first falling step in the
12514      same game frame, which is bad); this code is also needed to use the famous
12515      "spring push bug" which is used in older levels and might be wanted to be
12516      used also in newer levels, but in this case the buggy pushing code is only
12517      affecting the "spring" element and no other elements */
12518
12519   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12520   {
12521     for (i = 0; i < MAX_PLAYERS; i++)
12522     {
12523       struct PlayerInfo *player = &stored_player[i];
12524       int x = player->jx;
12525       int y = player->jy;
12526
12527       if (player->active && player->is_pushing && player->is_moving &&
12528           IS_MOVING(x, y) &&
12529           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12530            Feld[x][y] == EL_SPRING))
12531       {
12532         ContinueMoving(x, y);
12533
12534         /* continue moving after pushing (this is actually a bug) */
12535         if (!IS_MOVING(x, y))
12536           Stop[x][y] = FALSE;
12537       }
12538     }
12539   }
12540
12541 #if 0
12542   debug_print_timestamp(0, "start main loop profiling");
12543 #endif
12544
12545   SCAN_PLAYFIELD(x, y)
12546   {
12547     ChangeCount[x][y] = 0;
12548     ChangeEvent[x][y] = -1;
12549
12550     /* this must be handled before main playfield loop */
12551     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12552     {
12553       MovDelay[x][y]--;
12554       if (MovDelay[x][y] <= 0)
12555         RemoveField(x, y);
12556     }
12557
12558 #if USE_NEW_SNAP_DELAY
12559     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12560     {
12561       MovDelay[x][y]--;
12562       if (MovDelay[x][y] <= 0)
12563       {
12564         RemoveField(x, y);
12565         TEST_DrawLevelField(x, y);
12566
12567         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12568       }
12569     }
12570 #endif
12571
12572 #if DEBUG
12573     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12574     {
12575       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12576       printf("GameActions(): This should never happen!\n");
12577
12578       ChangePage[x][y] = -1;
12579     }
12580 #endif
12581
12582     Stop[x][y] = FALSE;
12583     if (WasJustMoving[x][y] > 0)
12584       WasJustMoving[x][y]--;
12585     if (WasJustFalling[x][y] > 0)
12586       WasJustFalling[x][y]--;
12587     if (CheckCollision[x][y] > 0)
12588       CheckCollision[x][y]--;
12589     if (CheckImpact[x][y] > 0)
12590       CheckImpact[x][y]--;
12591
12592     GfxFrame[x][y]++;
12593
12594     /* reset finished pushing action (not done in ContinueMoving() to allow
12595        continuous pushing animation for elements with zero push delay) */
12596     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12597     {
12598       ResetGfxAnimation(x, y);
12599       TEST_DrawLevelField(x, y);
12600     }
12601
12602 #if DEBUG
12603     if (IS_BLOCKED(x, y))
12604     {
12605       int oldx, oldy;
12606
12607       Blocked2Moving(x, y, &oldx, &oldy);
12608       if (!IS_MOVING(oldx, oldy))
12609       {
12610         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12611         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12612         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12613         printf("GameActions(): This should never happen!\n");
12614       }
12615     }
12616 #endif
12617   }
12618
12619 #if 0
12620   debug_print_timestamp(0, "- time for pre-main loop:");
12621 #endif
12622
12623 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12624   SCAN_PLAYFIELD(x, y)
12625   {
12626     element = Feld[x][y];
12627     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12628
12629 #if 1
12630     {
12631 #if 1
12632       int element2 = element;
12633       int graphic2 = graphic;
12634 #else
12635       int element2 = Feld[x][y];
12636       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12637 #endif
12638       int last_gfx_frame = GfxFrame[x][y];
12639
12640       if (graphic_info[graphic2].anim_global_sync)
12641         GfxFrame[x][y] = FrameCounter;
12642       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12643         GfxFrame[x][y] = CustomValue[x][y];
12644       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12645         GfxFrame[x][y] = element_info[element2].collect_score;
12646       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12647         GfxFrame[x][y] = ChangeDelay[x][y];
12648
12649       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12650         DrawLevelGraphicAnimation(x, y, graphic2);
12651     }
12652 #else
12653     ResetGfxFrame(x, y, TRUE);
12654 #endif
12655
12656 #if 1
12657     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12658         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12659       ResetRandomAnimationValue(x, y);
12660 #endif
12661
12662 #if 1
12663     SetRandomAnimationValue(x, y);
12664 #endif
12665
12666 #if 1
12667     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12668 #endif
12669   }
12670 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12671
12672 #if 0
12673   debug_print_timestamp(0, "- time for TEST loop:     -->");
12674 #endif
12675
12676   SCAN_PLAYFIELD(x, y)
12677   {
12678     element = Feld[x][y];
12679     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12680
12681     ResetGfxFrame(x, y, TRUE);
12682
12683     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12684         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12685       ResetRandomAnimationValue(x, y);
12686
12687     SetRandomAnimationValue(x, y);
12688
12689     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12690
12691     if (IS_INACTIVE(element))
12692     {
12693       if (IS_ANIMATED(graphic))
12694         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12695
12696       continue;
12697     }
12698
12699     /* this may take place after moving, so 'element' may have changed */
12700     if (IS_CHANGING(x, y) &&
12701         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12702     {
12703       int page = element_info[element].event_page_nr[CE_DELAY];
12704
12705 #if 1
12706       HandleElementChange(x, y, page);
12707 #else
12708       if (CAN_CHANGE(element))
12709         HandleElementChange(x, y, page);
12710
12711       if (HAS_ACTION(element))
12712         ExecuteCustomElementAction(x, y, element, page);
12713 #endif
12714
12715       element = Feld[x][y];
12716       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12717     }
12718
12719 #if 0   // ---------------------------------------------------------------------
12720
12721     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12722     {
12723       StartMoving(x, y);
12724
12725       element = Feld[x][y];
12726       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12727
12728       if (IS_ANIMATED(graphic) &&
12729           !IS_MOVING(x, y) &&
12730           !Stop[x][y])
12731         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12732
12733       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12734         TEST_DrawTwinkleOnField(x, y);
12735     }
12736     else if (IS_MOVING(x, y))
12737       ContinueMoving(x, y);
12738     else
12739     {
12740       switch (element)
12741       {
12742         case EL_ACID:
12743         case EL_EXIT_OPEN:
12744         case EL_EM_EXIT_OPEN:
12745         case EL_SP_EXIT_OPEN:
12746         case EL_STEEL_EXIT_OPEN:
12747         case EL_EM_STEEL_EXIT_OPEN:
12748         case EL_SP_TERMINAL:
12749         case EL_SP_TERMINAL_ACTIVE:
12750         case EL_EXTRA_TIME:
12751         case EL_SHIELD_NORMAL:
12752         case EL_SHIELD_DEADLY:
12753           if (IS_ANIMATED(graphic))
12754             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12755           break;
12756
12757         case EL_DYNAMITE_ACTIVE:
12758         case EL_EM_DYNAMITE_ACTIVE:
12759         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12760         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12761         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12762         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12763         case EL_SP_DISK_RED_ACTIVE:
12764           CheckDynamite(x, y);
12765           break;
12766
12767         case EL_AMOEBA_GROWING:
12768           AmoebeWaechst(x, y);
12769           break;
12770
12771         case EL_AMOEBA_SHRINKING:
12772           AmoebaDisappearing(x, y);
12773           break;
12774
12775 #if !USE_NEW_AMOEBA_CODE
12776         case EL_AMOEBA_WET:
12777         case EL_AMOEBA_DRY:
12778         case EL_AMOEBA_FULL:
12779         case EL_BD_AMOEBA:
12780         case EL_EMC_DRIPPER:
12781           AmoebeAbleger(x, y);
12782           break;
12783 #endif
12784
12785         case EL_GAME_OF_LIFE:
12786         case EL_BIOMAZE:
12787           Life(x, y);
12788           break;
12789
12790         case EL_EXIT_CLOSED:
12791           CheckExit(x, y);
12792           break;
12793
12794         case EL_EM_EXIT_CLOSED:
12795           CheckExitEM(x, y);
12796           break;
12797
12798         case EL_STEEL_EXIT_CLOSED:
12799           CheckExitSteel(x, y);
12800           break;
12801
12802         case EL_EM_STEEL_EXIT_CLOSED:
12803           CheckExitSteelEM(x, y);
12804           break;
12805
12806         case EL_SP_EXIT_CLOSED:
12807           CheckExitSP(x, y);
12808           break;
12809
12810         case EL_EXPANDABLE_WALL_GROWING:
12811         case EL_EXPANDABLE_STEELWALL_GROWING:
12812           MauerWaechst(x, y);
12813           break;
12814
12815         case EL_EXPANDABLE_WALL:
12816         case EL_EXPANDABLE_WALL_HORIZONTAL:
12817         case EL_EXPANDABLE_WALL_VERTICAL:
12818         case EL_EXPANDABLE_WALL_ANY:
12819         case EL_BD_EXPANDABLE_WALL:
12820           MauerAbleger(x, y);
12821           break;
12822
12823         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12824         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12825         case EL_EXPANDABLE_STEELWALL_ANY:
12826           MauerAblegerStahl(x, y);
12827           break;
12828
12829         case EL_FLAMES:
12830           CheckForDragon(x, y);
12831           break;
12832
12833         case EL_EXPLOSION:
12834           break;
12835
12836         case EL_ELEMENT_SNAPPING:
12837         case EL_DIAGONAL_SHRINKING:
12838         case EL_DIAGONAL_GROWING:
12839         {
12840           graphic =
12841             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12842
12843           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12844           break;
12845         }
12846
12847         default:
12848           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12849             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12850           break;
12851       }
12852     }
12853
12854 #else   // ---------------------------------------------------------------------
12855
12856     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12857     {
12858       StartMoving(x, y);
12859
12860       element = Feld[x][y];
12861       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12862
12863       if (IS_ANIMATED(graphic) &&
12864           !IS_MOVING(x, y) &&
12865           !Stop[x][y])
12866         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12867
12868       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12869         TEST_DrawTwinkleOnField(x, y);
12870     }
12871     else if ((element == EL_ACID ||
12872               element == EL_EXIT_OPEN ||
12873               element == EL_EM_EXIT_OPEN ||
12874               element == EL_SP_EXIT_OPEN ||
12875               element == EL_STEEL_EXIT_OPEN ||
12876               element == EL_EM_STEEL_EXIT_OPEN ||
12877               element == EL_SP_TERMINAL ||
12878               element == EL_SP_TERMINAL_ACTIVE ||
12879               element == EL_EXTRA_TIME ||
12880               element == EL_SHIELD_NORMAL ||
12881               element == EL_SHIELD_DEADLY) &&
12882              IS_ANIMATED(graphic))
12883       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12884     else if (IS_MOVING(x, y))
12885       ContinueMoving(x, y);
12886     else if (IS_ACTIVE_BOMB(element))
12887       CheckDynamite(x, y);
12888     else if (element == EL_AMOEBA_GROWING)
12889       AmoebeWaechst(x, y);
12890     else if (element == EL_AMOEBA_SHRINKING)
12891       AmoebaDisappearing(x, y);
12892
12893 #if !USE_NEW_AMOEBA_CODE
12894     else if (IS_AMOEBALIVE(element))
12895       AmoebeAbleger(x, y);
12896 #endif
12897
12898     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12899       Life(x, y);
12900     else if (element == EL_EXIT_CLOSED)
12901       CheckExit(x, y);
12902     else if (element == EL_EM_EXIT_CLOSED)
12903       CheckExitEM(x, y);
12904     else if (element == EL_STEEL_EXIT_CLOSED)
12905       CheckExitSteel(x, y);
12906     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12907       CheckExitSteelEM(x, y);
12908     else if (element == EL_SP_EXIT_CLOSED)
12909       CheckExitSP(x, y);
12910     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12911              element == EL_EXPANDABLE_STEELWALL_GROWING)
12912       MauerWaechst(x, y);
12913     else if (element == EL_EXPANDABLE_WALL ||
12914              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12915              element == EL_EXPANDABLE_WALL_VERTICAL ||
12916              element == EL_EXPANDABLE_WALL_ANY ||
12917              element == EL_BD_EXPANDABLE_WALL)
12918       MauerAbleger(x, y);
12919     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12920              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12921              element == EL_EXPANDABLE_STEELWALL_ANY)
12922       MauerAblegerStahl(x, y);
12923     else if (element == EL_FLAMES)
12924       CheckForDragon(x, y);
12925     else if (element == EL_EXPLOSION)
12926       ; /* drawing of correct explosion animation is handled separately */
12927     else if (element == EL_ELEMENT_SNAPPING ||
12928              element == EL_DIAGONAL_SHRINKING ||
12929              element == EL_DIAGONAL_GROWING)
12930     {
12931       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12932
12933       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12934     }
12935     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12936       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12937
12938 #endif  // ---------------------------------------------------------------------
12939
12940     if (IS_BELT_ACTIVE(element))
12941       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12942
12943     if (game.magic_wall_active)
12944     {
12945       int jx = local_player->jx, jy = local_player->jy;
12946
12947       /* play the element sound at the position nearest to the player */
12948       if ((element == EL_MAGIC_WALL_FULL ||
12949            element == EL_MAGIC_WALL_ACTIVE ||
12950            element == EL_MAGIC_WALL_EMPTYING ||
12951            element == EL_BD_MAGIC_WALL_FULL ||
12952            element == EL_BD_MAGIC_WALL_ACTIVE ||
12953            element == EL_BD_MAGIC_WALL_EMPTYING ||
12954            element == EL_DC_MAGIC_WALL_FULL ||
12955            element == EL_DC_MAGIC_WALL_ACTIVE ||
12956            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12957           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12958       {
12959         magic_wall_x = x;
12960         magic_wall_y = y;
12961       }
12962     }
12963   }
12964
12965 #if 0
12966   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12967 #endif
12968
12969 #if USE_NEW_AMOEBA_CODE
12970   /* new experimental amoeba growth stuff */
12971   if (!(FrameCounter % 8))
12972   {
12973     static unsigned long random = 1684108901;
12974
12975     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12976     {
12977       x = RND(lev_fieldx);
12978       y = RND(lev_fieldy);
12979       element = Feld[x][y];
12980
12981       if (!IS_PLAYER(x,y) &&
12982           (element == EL_EMPTY ||
12983            CAN_GROW_INTO(element) ||
12984            element == EL_QUICKSAND_EMPTY ||
12985            element == EL_QUICKSAND_FAST_EMPTY ||
12986            element == EL_ACID_SPLASH_LEFT ||
12987            element == EL_ACID_SPLASH_RIGHT))
12988       {
12989         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12990             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12991             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12992             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12993           Feld[x][y] = EL_AMOEBA_DROP;
12994       }
12995
12996       random = random * 129 + 1;
12997     }
12998   }
12999 #endif
13000
13001 #if 0
13002   if (game.explosions_delayed)
13003 #endif
13004   {
13005     game.explosions_delayed = FALSE;
13006
13007     SCAN_PLAYFIELD(x, y)
13008     {
13009       element = Feld[x][y];
13010
13011       if (ExplodeField[x][y])
13012         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13013       else if (element == EL_EXPLOSION)
13014         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13015
13016       ExplodeField[x][y] = EX_TYPE_NONE;
13017     }
13018
13019     game.explosions_delayed = TRUE;
13020   }
13021
13022   if (game.magic_wall_active)
13023   {
13024     if (!(game.magic_wall_time_left % 4))
13025     {
13026       int element = Feld[magic_wall_x][magic_wall_y];
13027
13028       if (element == EL_BD_MAGIC_WALL_FULL ||
13029           element == EL_BD_MAGIC_WALL_ACTIVE ||
13030           element == EL_BD_MAGIC_WALL_EMPTYING)
13031         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13032       else if (element == EL_DC_MAGIC_WALL_FULL ||
13033                element == EL_DC_MAGIC_WALL_ACTIVE ||
13034                element == EL_DC_MAGIC_WALL_EMPTYING)
13035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13036       else
13037         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13038     }
13039
13040     if (game.magic_wall_time_left > 0)
13041     {
13042       game.magic_wall_time_left--;
13043
13044       if (!game.magic_wall_time_left)
13045       {
13046         SCAN_PLAYFIELD(x, y)
13047         {
13048           element = Feld[x][y];
13049
13050           if (element == EL_MAGIC_WALL_ACTIVE ||
13051               element == EL_MAGIC_WALL_FULL)
13052           {
13053             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13054             TEST_DrawLevelField(x, y);
13055           }
13056           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13057                    element == EL_BD_MAGIC_WALL_FULL)
13058           {
13059             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13060             TEST_DrawLevelField(x, y);
13061           }
13062           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13063                    element == EL_DC_MAGIC_WALL_FULL)
13064           {
13065             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13066             TEST_DrawLevelField(x, y);
13067           }
13068         }
13069
13070         game.magic_wall_active = FALSE;
13071       }
13072     }
13073   }
13074
13075   if (game.light_time_left > 0)
13076   {
13077     game.light_time_left--;
13078
13079     if (game.light_time_left == 0)
13080       RedrawAllLightSwitchesAndInvisibleElements();
13081   }
13082
13083   if (game.timegate_time_left > 0)
13084   {
13085     game.timegate_time_left--;
13086
13087     if (game.timegate_time_left == 0)
13088       CloseAllOpenTimegates();
13089   }
13090
13091   if (game.lenses_time_left > 0)
13092   {
13093     game.lenses_time_left--;
13094
13095     if (game.lenses_time_left == 0)
13096       RedrawAllInvisibleElementsForLenses();
13097   }
13098
13099   if (game.magnify_time_left > 0)
13100   {
13101     game.magnify_time_left--;
13102
13103     if (game.magnify_time_left == 0)
13104       RedrawAllInvisibleElementsForMagnifier();
13105   }
13106
13107   for (i = 0; i < MAX_PLAYERS; i++)
13108   {
13109     struct PlayerInfo *player = &stored_player[i];
13110
13111     if (SHIELD_ON(player))
13112     {
13113       if (player->shield_deadly_time_left)
13114         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13115       else if (player->shield_normal_time_left)
13116         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13117     }
13118   }
13119
13120 #if USE_DELAYED_GFX_REDRAW
13121   SCAN_PLAYFIELD(x, y)
13122   {
13123 #if 1
13124     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13125 #else
13126     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13127         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13128 #endif
13129     {
13130       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13131          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13132
13133       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13134         DrawLevelField(x, y);
13135
13136       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13137         DrawLevelFieldCrumbledSand(x, y);
13138
13139       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13140         DrawLevelFieldCrumbledSandNeighbours(x, y);
13141
13142       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13143         DrawTwinkleOnField(x, y);
13144     }
13145
13146     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13147   }
13148 #endif
13149
13150   CheckLevelTime();
13151
13152   DrawAllPlayers();
13153   PlayAllPlayersSound();
13154
13155   if (options.debug)                    /* calculate frames per second */
13156   {
13157     static unsigned long fps_counter = 0;
13158     static int fps_frames = 0;
13159     unsigned long fps_delay_ms = Counter() - fps_counter;
13160
13161     fps_frames++;
13162
13163     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13164     {
13165       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13166
13167       fps_frames = 0;
13168       fps_counter = Counter();
13169     }
13170
13171     redraw_mask |= REDRAW_FPS;
13172   }
13173
13174   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13175
13176   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13177   {
13178     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13179
13180     local_player->show_envelope = 0;
13181   }
13182
13183 #if 0
13184   debug_print_timestamp(0, "stop main loop profiling ");
13185   printf("----------------------------------------------------------\n");
13186 #endif
13187
13188   /* use random number generator in every frame to make it less predictable */
13189   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13190     RND(1);
13191 }
13192
13193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13194 {
13195   int min_x = x, min_y = y, max_x = x, max_y = y;
13196   int i;
13197
13198   for (i = 0; i < MAX_PLAYERS; i++)
13199   {
13200     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13201
13202     if (!stored_player[i].active || &stored_player[i] == player)
13203       continue;
13204
13205     min_x = MIN(min_x, jx);
13206     min_y = MIN(min_y, jy);
13207     max_x = MAX(max_x, jx);
13208     max_y = MAX(max_y, jy);
13209   }
13210
13211   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13212 }
13213
13214 static boolean AllPlayersInVisibleScreen()
13215 {
13216   int i;
13217
13218   for (i = 0; i < MAX_PLAYERS; i++)
13219   {
13220     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13221
13222     if (!stored_player[i].active)
13223       continue;
13224
13225     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13226       return FALSE;
13227   }
13228
13229   return TRUE;
13230 }
13231
13232 void ScrollLevel(int dx, int dy)
13233 {
13234 #if 0
13235   /* (directly solved in BlitBitmap() now) */
13236   static Bitmap *bitmap_db_field2 = NULL;
13237   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13238   int x, y;
13239 #else
13240   int x, y;
13241 #endif
13242
13243 #if 0
13244   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13245   /* only horizontal XOR vertical scroll direction allowed */
13246   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13247     return;
13248 #endif
13249
13250 #if 0
13251   /* (directly solved in BlitBitmap() now) */
13252   if (bitmap_db_field2 == NULL)
13253     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13254
13255   /* needed when blitting directly to same bitmap -- should not be needed with
13256      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13257   BlitBitmap(drawto_field, bitmap_db_field2,
13258              FX + TILEX * (dx == -1) - softscroll_offset,
13259              FY + TILEY * (dy == -1) - softscroll_offset,
13260              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13261              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13262              FX + TILEX * (dx == 1) - softscroll_offset,
13263              FY + TILEY * (dy == 1) - softscroll_offset);
13264   BlitBitmap(bitmap_db_field2, drawto_field,
13265              FX + TILEX * (dx == 1) - softscroll_offset,
13266              FY + TILEY * (dy == 1) - softscroll_offset,
13267              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13268              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13269              FX + TILEX * (dx == 1) - softscroll_offset,
13270              FY + TILEY * (dy == 1) - softscroll_offset);
13271
13272 #else
13273
13274 #if 0
13275   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13276   int xsize = (BX2 - BX1 + 1);
13277   int ysize = (BY2 - BY1 + 1);
13278   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13279   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13280   int step  = (start < end ? +1 : -1);
13281
13282   for (i = start; i != end; i += step)
13283   {
13284     BlitBitmap(drawto_field, drawto_field,
13285                FX + TILEX * (dx != 0 ? i + step : 0),
13286                FY + TILEY * (dy != 0 ? i + step : 0),
13287                TILEX * (dx != 0 ? 1 : xsize),
13288                TILEY * (dy != 0 ? 1 : ysize),
13289                FX + TILEX * (dx != 0 ? i : 0),
13290                FY + TILEY * (dy != 0 ? i : 0));
13291   }
13292
13293 #else
13294
13295   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13296
13297   BlitBitmap(drawto_field, drawto_field,
13298              FX + TILEX * (dx == -1) - softscroll_offset,
13299              FY + TILEY * (dy == -1) - softscroll_offset,
13300              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13301              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13302              FX + TILEX * (dx == 1) - softscroll_offset,
13303              FY + TILEY * (dy == 1) - softscroll_offset);
13304 #endif
13305 #endif
13306
13307   if (dx != 0)
13308   {
13309     x = (dx == 1 ? BX1 : BX2);
13310     for (y = BY1; y <= BY2; y++)
13311       DrawScreenField(x, y);
13312   }
13313
13314   if (dy != 0)
13315   {
13316     y = (dy == 1 ? BY1 : BY2);
13317     for (x = BX1; x <= BX2; x++)
13318       DrawScreenField(x, y);
13319   }
13320
13321   redraw_mask |= REDRAW_FIELD;
13322 }
13323
13324 static boolean canFallDown(struct PlayerInfo *player)
13325 {
13326   int jx = player->jx, jy = player->jy;
13327
13328   return (IN_LEV_FIELD(jx, jy + 1) &&
13329           (IS_FREE(jx, jy + 1) ||
13330            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13331           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13332           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13333 }
13334
13335 static boolean canPassField(int x, int y, int move_dir)
13336 {
13337   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13338   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13339   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13340   int nextx = x + dx;
13341   int nexty = y + dy;
13342   int element = Feld[x][y];
13343
13344   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13345           !CAN_MOVE(element) &&
13346           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13347           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13348           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13349 }
13350
13351 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13352 {
13353   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13354   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13355   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13356   int newx = x + dx;
13357   int newy = y + dy;
13358
13359   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13360           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13361           (IS_DIGGABLE(Feld[newx][newy]) ||
13362            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13363            canPassField(newx, newy, move_dir)));
13364 }
13365
13366 static void CheckGravityMovement(struct PlayerInfo *player)
13367 {
13368 #if USE_PLAYER_GRAVITY
13369   if (player->gravity && !player->programmed_action)
13370 #else
13371   if (game.gravity && !player->programmed_action)
13372 #endif
13373   {
13374     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13375     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13376     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13377     int jx = player->jx, jy = player->jy;
13378     boolean player_is_moving_to_valid_field =
13379       (!player_is_snapping &&
13380        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13381         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13382     boolean player_can_fall_down = canFallDown(player);
13383
13384     if (player_can_fall_down &&
13385         !player_is_moving_to_valid_field)
13386       player->programmed_action = MV_DOWN;
13387   }
13388 }
13389
13390 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13391 {
13392   return CheckGravityMovement(player);
13393
13394 #if USE_PLAYER_GRAVITY
13395   if (player->gravity && !player->programmed_action)
13396 #else
13397   if (game.gravity && !player->programmed_action)
13398 #endif
13399   {
13400     int jx = player->jx, jy = player->jy;
13401     boolean field_under_player_is_free =
13402       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13403     boolean player_is_standing_on_valid_field =
13404       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13405        (IS_WALKABLE(Feld[jx][jy]) &&
13406         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13407
13408     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13409       player->programmed_action = MV_DOWN;
13410   }
13411 }
13412
13413 /*
13414   MovePlayerOneStep()
13415   -----------------------------------------------------------------------------
13416   dx, dy:               direction (non-diagonal) to try to move the player to
13417   real_dx, real_dy:     direction as read from input device (can be diagonal)
13418 */
13419
13420 boolean MovePlayerOneStep(struct PlayerInfo *player,
13421                           int dx, int dy, int real_dx, int real_dy)
13422 {
13423   int jx = player->jx, jy = player->jy;
13424   int new_jx = jx + dx, new_jy = jy + dy;
13425 #if !USE_FIXED_DONT_RUN_INTO
13426   int element;
13427 #endif
13428   int can_move;
13429   boolean player_can_move = !player->cannot_move;
13430
13431   if (!player->active || (!dx && !dy))
13432     return MP_NO_ACTION;
13433
13434   player->MovDir = (dx < 0 ? MV_LEFT :
13435                     dx > 0 ? MV_RIGHT :
13436                     dy < 0 ? MV_UP :
13437                     dy > 0 ? MV_DOWN :  MV_NONE);
13438
13439   if (!IN_LEV_FIELD(new_jx, new_jy))
13440     return MP_NO_ACTION;
13441
13442   if (!player_can_move)
13443   {
13444     if (player->MovPos == 0)
13445     {
13446       player->is_moving = FALSE;
13447       player->is_digging = FALSE;
13448       player->is_collecting = FALSE;
13449       player->is_snapping = FALSE;
13450       player->is_pushing = FALSE;
13451     }
13452   }
13453
13454 #if 1
13455   if (!options.network && game.centered_player_nr == -1 &&
13456       !AllPlayersInSight(player, new_jx, new_jy))
13457     return MP_NO_ACTION;
13458 #else
13459   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13460     return MP_NO_ACTION;
13461 #endif
13462
13463 #if !USE_FIXED_DONT_RUN_INTO
13464   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13465
13466   /* (moved to DigField()) */
13467   if (player_can_move && DONT_RUN_INTO(element))
13468   {
13469     if (element == EL_ACID && dx == 0 && dy == 1)
13470     {
13471       SplashAcid(new_jx, new_jy);
13472       Feld[jx][jy] = EL_PLAYER_1;
13473       InitMovingField(jx, jy, MV_DOWN);
13474       Store[jx][jy] = EL_ACID;
13475       ContinueMoving(jx, jy);
13476       BuryPlayer(player);
13477     }
13478     else
13479       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13480
13481     return MP_MOVING;
13482   }
13483 #endif
13484
13485   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13486   if (can_move != MP_MOVING)
13487     return can_move;
13488
13489   /* check if DigField() has caused relocation of the player */
13490   if (player->jx != jx || player->jy != jy)
13491     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13492
13493   StorePlayer[jx][jy] = 0;
13494   player->last_jx = jx;
13495   player->last_jy = jy;
13496   player->jx = new_jx;
13497   player->jy = new_jy;
13498   StorePlayer[new_jx][new_jy] = player->element_nr;
13499
13500   if (player->move_delay_value_next != -1)
13501   {
13502     player->move_delay_value = player->move_delay_value_next;
13503     player->move_delay_value_next = -1;
13504   }
13505
13506   player->MovPos =
13507     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13508
13509   player->step_counter++;
13510
13511   PlayerVisit[jx][jy] = FrameCounter;
13512
13513 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13514   player->is_moving = TRUE;
13515 #endif
13516
13517 #if 1
13518   /* should better be called in MovePlayer(), but this breaks some tapes */
13519   ScrollPlayer(player, SCROLL_INIT);
13520 #endif
13521
13522   return MP_MOVING;
13523 }
13524
13525 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13526 {
13527   int jx = player->jx, jy = player->jy;
13528   int old_jx = jx, old_jy = jy;
13529   int moved = MP_NO_ACTION;
13530
13531   if (!player->active)
13532     return FALSE;
13533
13534   if (!dx && !dy)
13535   {
13536     if (player->MovPos == 0)
13537     {
13538       player->is_moving = FALSE;
13539       player->is_digging = FALSE;
13540       player->is_collecting = FALSE;
13541       player->is_snapping = FALSE;
13542       player->is_pushing = FALSE;
13543     }
13544
13545     return FALSE;
13546   }
13547
13548   if (player->move_delay > 0)
13549     return FALSE;
13550
13551   player->move_delay = -1;              /* set to "uninitialized" value */
13552
13553   /* store if player is automatically moved to next field */
13554   player->is_auto_moving = (player->programmed_action != MV_NONE);
13555
13556   /* remove the last programmed player action */
13557   player->programmed_action = 0;
13558
13559   if (player->MovPos)
13560   {
13561     /* should only happen if pre-1.2 tape recordings are played */
13562     /* this is only for backward compatibility */
13563
13564     int original_move_delay_value = player->move_delay_value;
13565
13566 #if DEBUG
13567     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13568            tape.counter);
13569 #endif
13570
13571     /* scroll remaining steps with finest movement resolution */
13572     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13573
13574     while (player->MovPos)
13575     {
13576       ScrollPlayer(player, SCROLL_GO_ON);
13577       ScrollScreen(NULL, SCROLL_GO_ON);
13578
13579       AdvanceFrameAndPlayerCounters(player->index_nr);
13580
13581       DrawAllPlayers();
13582       BackToFront();
13583     }
13584
13585     player->move_delay_value = original_move_delay_value;
13586   }
13587
13588   player->is_active = FALSE;
13589
13590   if (player->last_move_dir & MV_HORIZONTAL)
13591   {
13592     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13593       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13594   }
13595   else
13596   {
13597     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13598       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13599   }
13600
13601 #if USE_FIXED_BORDER_RUNNING_GFX
13602   if (!moved && !player->is_active)
13603   {
13604     player->is_moving = FALSE;
13605     player->is_digging = FALSE;
13606     player->is_collecting = FALSE;
13607     player->is_snapping = FALSE;
13608     player->is_pushing = FALSE;
13609   }
13610 #endif
13611
13612   jx = player->jx;
13613   jy = player->jy;
13614
13615 #if 1
13616   if (moved & MP_MOVING && !ScreenMovPos &&
13617       (player->index_nr == game.centered_player_nr ||
13618        game.centered_player_nr == -1))
13619 #else
13620   if (moved & MP_MOVING && !ScreenMovPos &&
13621       (player == local_player || !options.network))
13622 #endif
13623   {
13624     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13625     int offset = game.scroll_delay_value;
13626
13627     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13628     {
13629       /* actual player has left the screen -- scroll in that direction */
13630       if (jx != old_jx)         /* player has moved horizontally */
13631         scroll_x += (jx - old_jx);
13632       else                      /* player has moved vertically */
13633         scroll_y += (jy - old_jy);
13634     }
13635     else
13636     {
13637       if (jx != old_jx)         /* player has moved horizontally */
13638       {
13639         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13640             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13641           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13642
13643         /* don't scroll over playfield boundaries */
13644         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13645           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13646
13647         /* don't scroll more than one field at a time */
13648         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13649
13650         /* don't scroll against the player's moving direction */
13651         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13652             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13653           scroll_x = old_scroll_x;
13654       }
13655       else                      /* player has moved vertically */
13656       {
13657         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13658             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13659           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13660
13661         /* don't scroll over playfield boundaries */
13662         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13663           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13664
13665         /* don't scroll more than one field at a time */
13666         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13667
13668         /* don't scroll against the player's moving direction */
13669         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13670             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13671           scroll_y = old_scroll_y;
13672       }
13673     }
13674
13675     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13676     {
13677 #if 1
13678       if (!options.network && game.centered_player_nr == -1 &&
13679           !AllPlayersInVisibleScreen())
13680       {
13681         scroll_x = old_scroll_x;
13682         scroll_y = old_scroll_y;
13683       }
13684       else
13685 #else
13686       if (!options.network && !AllPlayersInVisibleScreen())
13687       {
13688         scroll_x = old_scroll_x;
13689         scroll_y = old_scroll_y;
13690       }
13691       else
13692 #endif
13693       {
13694         ScrollScreen(player, SCROLL_INIT);
13695         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13696       }
13697     }
13698   }
13699
13700   player->StepFrame = 0;
13701
13702   if (moved & MP_MOVING)
13703   {
13704     if (old_jx != jx && old_jy == jy)
13705       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13706     else if (old_jx == jx && old_jy != jy)
13707       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13708
13709     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13710
13711     player->last_move_dir = player->MovDir;
13712     player->is_moving = TRUE;
13713     player->is_snapping = FALSE;
13714     player->is_switching = FALSE;
13715     player->is_dropping = FALSE;
13716     player->is_dropping_pressed = FALSE;
13717     player->drop_pressed_delay = 0;
13718
13719 #if 0
13720     /* should better be called here than above, but this breaks some tapes */
13721     ScrollPlayer(player, SCROLL_INIT);
13722 #endif
13723   }
13724   else
13725   {
13726     CheckGravityMovementWhenNotMoving(player);
13727
13728     player->is_moving = FALSE;
13729
13730     /* at this point, the player is allowed to move, but cannot move right now
13731        (e.g. because of something blocking the way) -- ensure that the player
13732        is also allowed to move in the next frame (in old versions before 3.1.1,
13733        the player was forced to wait again for eight frames before next try) */
13734
13735     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13736       player->move_delay = 0;   /* allow direct movement in the next frame */
13737   }
13738
13739   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13740     player->move_delay = player->move_delay_value;
13741
13742   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13743   {
13744     TestIfPlayerTouchesBadThing(jx, jy);
13745     TestIfPlayerTouchesCustomElement(jx, jy);
13746   }
13747
13748   if (!player->active)
13749     RemovePlayer(player);
13750
13751   return moved;
13752 }
13753
13754 void ScrollPlayer(struct PlayerInfo *player, int mode)
13755 {
13756   int jx = player->jx, jy = player->jy;
13757   int last_jx = player->last_jx, last_jy = player->last_jy;
13758   int move_stepsize = TILEX / player->move_delay_value;
13759
13760 #if USE_NEW_PLAYER_SPEED
13761   if (!player->active)
13762     return;
13763
13764   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13765     return;
13766 #else
13767   if (!player->active || player->MovPos == 0)
13768     return;
13769 #endif
13770
13771   if (mode == SCROLL_INIT)
13772   {
13773     player->actual_frame_counter = FrameCounter;
13774     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13775
13776     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13777         Feld[last_jx][last_jy] == EL_EMPTY)
13778     {
13779       int last_field_block_delay = 0;   /* start with no blocking at all */
13780       int block_delay_adjustment = player->block_delay_adjustment;
13781
13782       /* if player blocks last field, add delay for exactly one move */
13783       if (player->block_last_field)
13784       {
13785         last_field_block_delay += player->move_delay_value;
13786
13787         /* when blocking enabled, prevent moving up despite gravity */
13788 #if USE_PLAYER_GRAVITY
13789         if (player->gravity && player->MovDir == MV_UP)
13790           block_delay_adjustment = -1;
13791 #else
13792         if (game.gravity && player->MovDir == MV_UP)
13793           block_delay_adjustment = -1;
13794 #endif
13795       }
13796
13797       /* add block delay adjustment (also possible when not blocking) */
13798       last_field_block_delay += block_delay_adjustment;
13799
13800       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13801       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13802     }
13803
13804 #if USE_NEW_PLAYER_SPEED
13805     if (player->MovPos != 0)    /* player has not yet reached destination */
13806       return;
13807 #else
13808     return;
13809 #endif
13810   }
13811   else if (!FrameReached(&player->actual_frame_counter, 1))
13812     return;
13813
13814 #if USE_NEW_PLAYER_SPEED
13815   if (player->MovPos != 0)
13816   {
13817     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13818     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13819
13820     /* before DrawPlayer() to draw correct player graphic for this case */
13821     if (player->MovPos == 0)
13822       CheckGravityMovement(player);
13823   }
13824 #else
13825   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13826   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13827
13828   /* before DrawPlayer() to draw correct player graphic for this case */
13829   if (player->MovPos == 0)
13830     CheckGravityMovement(player);
13831 #endif
13832
13833   if (player->MovPos == 0)      /* player reached destination field */
13834   {
13835     if (player->move_delay_reset_counter > 0)
13836     {
13837       player->move_delay_reset_counter--;
13838
13839       if (player->move_delay_reset_counter == 0)
13840       {
13841         /* continue with normal speed after quickly moving through gate */
13842         HALVE_PLAYER_SPEED(player);
13843
13844         /* be able to make the next move without delay */
13845         player->move_delay = 0;
13846       }
13847     }
13848
13849     player->last_jx = jx;
13850     player->last_jy = jy;
13851
13852     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13853         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13854 #if 1
13855         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13856 #endif
13857         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13858         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13859 #if 1
13860         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13861 #endif
13862         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13863         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13864     {
13865       DrawPlayer(player);       /* needed here only to cleanup last field */
13866       RemovePlayer(player);
13867
13868       if (local_player->friends_still_needed == 0 ||
13869           IS_SP_ELEMENT(Feld[jx][jy]))
13870         PlayerWins(player);
13871     }
13872
13873     /* this breaks one level: "machine", level 000 */
13874     {
13875       int move_direction = player->MovDir;
13876       int enter_side = MV_DIR_OPPOSITE(move_direction);
13877       int leave_side = move_direction;
13878       int old_jx = last_jx;
13879       int old_jy = last_jy;
13880       int old_element = Feld[old_jx][old_jy];
13881       int new_element = Feld[jx][jy];
13882
13883       if (IS_CUSTOM_ELEMENT(old_element))
13884         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13885                                    CE_LEFT_BY_PLAYER,
13886                                    player->index_bit, leave_side);
13887
13888       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13889                                           CE_PLAYER_LEAVES_X,
13890                                           player->index_bit, leave_side);
13891
13892       if (IS_CUSTOM_ELEMENT(new_element))
13893         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13894                                    player->index_bit, enter_side);
13895
13896       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13897                                           CE_PLAYER_ENTERS_X,
13898                                           player->index_bit, enter_side);
13899
13900 #if USE_FIX_CE_ACTION_WITH_PLAYER
13901       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13902                                         CE_MOVE_OF_X, move_direction);
13903 #else
13904       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13905                                         CE_MOVE_OF_X, move_direction);
13906 #endif
13907     }
13908
13909     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13910     {
13911       TestIfPlayerTouchesBadThing(jx, jy);
13912       TestIfPlayerTouchesCustomElement(jx, jy);
13913
13914       /* needed because pushed element has not yet reached its destination,
13915          so it would trigger a change event at its previous field location */
13916       if (!player->is_pushing)
13917         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13918
13919       if (!player->active)
13920         RemovePlayer(player);
13921     }
13922
13923     if (!local_player->LevelSolved && level.use_step_counter)
13924     {
13925       int i;
13926
13927       TimePlayed++;
13928
13929       if (TimeLeft > 0)
13930       {
13931         TimeLeft--;
13932
13933         if (TimeLeft <= 10 && setup.time_limit)
13934           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13935
13936 #if 1
13937         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13938
13939         DisplayGameControlValues();
13940 #else
13941         DrawGameValue_Time(TimeLeft);
13942 #endif
13943
13944         if (!TimeLeft && setup.time_limit)
13945           for (i = 0; i < MAX_PLAYERS; i++)
13946             KillPlayer(&stored_player[i]);
13947       }
13948 #if 1
13949       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13950       {
13951         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13952
13953         DisplayGameControlValues();
13954       }
13955 #else
13956       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13957         DrawGameValue_Time(TimePlayed);
13958 #endif
13959     }
13960
13961     if (tape.single_step && tape.recording && !tape.pausing &&
13962         !player->programmed_action)
13963       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13964   }
13965 }
13966
13967 void ScrollScreen(struct PlayerInfo *player, int mode)
13968 {
13969   static unsigned long screen_frame_counter = 0;
13970
13971   if (mode == SCROLL_INIT)
13972   {
13973     /* set scrolling step size according to actual player's moving speed */
13974     ScrollStepSize = TILEX / player->move_delay_value;
13975
13976     screen_frame_counter = FrameCounter;
13977     ScreenMovDir = player->MovDir;
13978     ScreenMovPos = player->MovPos;
13979     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13980     return;
13981   }
13982   else if (!FrameReached(&screen_frame_counter, 1))
13983     return;
13984
13985   if (ScreenMovPos)
13986   {
13987     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13988     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13989     redraw_mask |= REDRAW_FIELD;
13990   }
13991   else
13992     ScreenMovDir = MV_NONE;
13993 }
13994
13995 void TestIfPlayerTouchesCustomElement(int x, int y)
13996 {
13997   static int xy[4][2] =
13998   {
13999     { 0, -1 },
14000     { -1, 0 },
14001     { +1, 0 },
14002     { 0, +1 }
14003   };
14004   static int trigger_sides[4][2] =
14005   {
14006     /* center side       border side */
14007     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14008     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14009     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14010     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14011   };
14012   static int touch_dir[4] =
14013   {
14014     MV_LEFT | MV_RIGHT,
14015     MV_UP   | MV_DOWN,
14016     MV_UP   | MV_DOWN,
14017     MV_LEFT | MV_RIGHT
14018   };
14019   int center_element = Feld[x][y];      /* should always be non-moving! */
14020   int i;
14021
14022   for (i = 0; i < NUM_DIRECTIONS; i++)
14023   {
14024     int xx = x + xy[i][0];
14025     int yy = y + xy[i][1];
14026     int center_side = trigger_sides[i][0];
14027     int border_side = trigger_sides[i][1];
14028     int border_element;
14029
14030     if (!IN_LEV_FIELD(xx, yy))
14031       continue;
14032
14033     if (IS_PLAYER(x, y))                /* player found at center element */
14034     {
14035       struct PlayerInfo *player = PLAYERINFO(x, y);
14036
14037       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14038         border_element = Feld[xx][yy];          /* may be moving! */
14039       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14040         border_element = Feld[xx][yy];
14041       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14042         border_element = MovingOrBlocked2Element(xx, yy);
14043       else
14044         continue;               /* center and border element do not touch */
14045
14046       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14047                                  player->index_bit, border_side);
14048       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14049                                           CE_PLAYER_TOUCHES_X,
14050                                           player->index_bit, border_side);
14051
14052 #if USE_FIX_CE_ACTION_WITH_PLAYER
14053       {
14054         /* use player element that is initially defined in the level playfield,
14055            not the player element that corresponds to the runtime player number
14056            (example: a level that contains EL_PLAYER_3 as the only player would
14057            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14058         int player_element = PLAYERINFO(x, y)->initial_element;
14059
14060         CheckElementChangeBySide(xx, yy, border_element, player_element,
14061                                  CE_TOUCHING_X, border_side);
14062       }
14063 #endif
14064     }
14065     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14066     {
14067       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14068
14069       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14070       {
14071         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14072           continue;             /* center and border element do not touch */
14073       }
14074
14075       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14076                                  player->index_bit, center_side);
14077       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14078                                           CE_PLAYER_TOUCHES_X,
14079                                           player->index_bit, center_side);
14080
14081 #if USE_FIX_CE_ACTION_WITH_PLAYER
14082       {
14083         /* use player element that is initially defined in the level playfield,
14084            not the player element that corresponds to the runtime player number
14085            (example: a level that contains EL_PLAYER_3 as the only player would
14086            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14087         int player_element = PLAYERINFO(xx, yy)->initial_element;
14088
14089         CheckElementChangeBySide(x, y, center_element, player_element,
14090                                  CE_TOUCHING_X, center_side);
14091       }
14092 #endif
14093
14094       break;
14095     }
14096   }
14097 }
14098
14099 #if USE_ELEMENT_TOUCHING_BUGFIX
14100
14101 void TestIfElementTouchesCustomElement(int x, int y)
14102 {
14103   static int xy[4][2] =
14104   {
14105     { 0, -1 },
14106     { -1, 0 },
14107     { +1, 0 },
14108     { 0, +1 }
14109   };
14110   static int trigger_sides[4][2] =
14111   {
14112     /* center side      border side */
14113     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14114     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14115     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14116     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14117   };
14118   static int touch_dir[4] =
14119   {
14120     MV_LEFT | MV_RIGHT,
14121     MV_UP   | MV_DOWN,
14122     MV_UP   | MV_DOWN,
14123     MV_LEFT | MV_RIGHT
14124   };
14125   boolean change_center_element = FALSE;
14126   int center_element = Feld[x][y];      /* should always be non-moving! */
14127   int border_element_old[NUM_DIRECTIONS];
14128   int i;
14129
14130   for (i = 0; i < NUM_DIRECTIONS; i++)
14131   {
14132     int xx = x + xy[i][0];
14133     int yy = y + xy[i][1];
14134     int border_element;
14135
14136     border_element_old[i] = -1;
14137
14138     if (!IN_LEV_FIELD(xx, yy))
14139       continue;
14140
14141     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14142       border_element = Feld[xx][yy];    /* may be moving! */
14143     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14144       border_element = Feld[xx][yy];
14145     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14146       border_element = MovingOrBlocked2Element(xx, yy);
14147     else
14148       continue;                 /* center and border element do not touch */
14149
14150     border_element_old[i] = border_element;
14151   }
14152
14153   for (i = 0; i < NUM_DIRECTIONS; i++)
14154   {
14155     int xx = x + xy[i][0];
14156     int yy = y + xy[i][1];
14157     int center_side = trigger_sides[i][0];
14158     int border_element = border_element_old[i];
14159
14160     if (border_element == -1)
14161       continue;
14162
14163     /* check for change of border element */
14164     CheckElementChangeBySide(xx, yy, border_element, center_element,
14165                              CE_TOUCHING_X, center_side);
14166
14167     /* (center element cannot be player, so we dont have to check this here) */
14168   }
14169
14170   for (i = 0; i < NUM_DIRECTIONS; i++)
14171   {
14172     int xx = x + xy[i][0];
14173     int yy = y + xy[i][1];
14174     int border_side = trigger_sides[i][1];
14175     int border_element = border_element_old[i];
14176
14177     if (border_element == -1)
14178       continue;
14179
14180     /* check for change of center element (but change it only once) */
14181     if (!change_center_element)
14182       change_center_element =
14183         CheckElementChangeBySide(x, y, center_element, border_element,
14184                                  CE_TOUCHING_X, border_side);
14185
14186 #if USE_FIX_CE_ACTION_WITH_PLAYER
14187     if (IS_PLAYER(xx, yy))
14188     {
14189       /* use player element that is initially defined in the level playfield,
14190          not the player element that corresponds to the runtime player number
14191          (example: a level that contains EL_PLAYER_3 as the only player would
14192          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14193       int player_element = PLAYERINFO(xx, yy)->initial_element;
14194
14195       CheckElementChangeBySide(x, y, center_element, player_element,
14196                                CE_TOUCHING_X, border_side);
14197     }
14198 #endif
14199   }
14200 }
14201
14202 #else
14203
14204 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14205 {
14206   static int xy[4][2] =
14207   {
14208     { 0, -1 },
14209     { -1, 0 },
14210     { +1, 0 },
14211     { 0, +1 }
14212   };
14213   static int trigger_sides[4][2] =
14214   {
14215     /* center side      border side */
14216     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14217     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14218     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14219     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14220   };
14221   static int touch_dir[4] =
14222   {
14223     MV_LEFT | MV_RIGHT,
14224     MV_UP   | MV_DOWN,
14225     MV_UP   | MV_DOWN,
14226     MV_LEFT | MV_RIGHT
14227   };
14228   boolean change_center_element = FALSE;
14229   int center_element = Feld[x][y];      /* should always be non-moving! */
14230   int i;
14231
14232   for (i = 0; i < NUM_DIRECTIONS; i++)
14233   {
14234     int xx = x + xy[i][0];
14235     int yy = y + xy[i][1];
14236     int center_side = trigger_sides[i][0];
14237     int border_side = trigger_sides[i][1];
14238     int border_element;
14239
14240     if (!IN_LEV_FIELD(xx, yy))
14241       continue;
14242
14243     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14244       border_element = Feld[xx][yy];    /* may be moving! */
14245     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14246       border_element = Feld[xx][yy];
14247     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14248       border_element = MovingOrBlocked2Element(xx, yy);
14249     else
14250       continue;                 /* center and border element do not touch */
14251
14252     /* check for change of center element (but change it only once) */
14253     if (!change_center_element)
14254       change_center_element =
14255         CheckElementChangeBySide(x, y, center_element, border_element,
14256                                  CE_TOUCHING_X, border_side);
14257
14258     /* check for change of border element */
14259     CheckElementChangeBySide(xx, yy, border_element, center_element,
14260                              CE_TOUCHING_X, center_side);
14261   }
14262 }
14263
14264 #endif
14265
14266 void TestIfElementHitsCustomElement(int x, int y, int direction)
14267 {
14268   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14269   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14270   int hitx = x + dx, hity = y + dy;
14271   int hitting_element = Feld[x][y];
14272   int touched_element;
14273
14274   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14275     return;
14276
14277   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14278                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14279
14280   if (IN_LEV_FIELD(hitx, hity))
14281   {
14282     int opposite_direction = MV_DIR_OPPOSITE(direction);
14283     int hitting_side = direction;
14284     int touched_side = opposite_direction;
14285     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14286                           MovDir[hitx][hity] != direction ||
14287                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14288
14289     object_hit = TRUE;
14290
14291     if (object_hit)
14292     {
14293       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14294                                CE_HITTING_X, touched_side);
14295
14296       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14297                                CE_HIT_BY_X, hitting_side);
14298
14299       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14300                                CE_HIT_BY_SOMETHING, opposite_direction);
14301
14302 #if USE_FIX_CE_ACTION_WITH_PLAYER
14303       if (IS_PLAYER(hitx, hity))
14304       {
14305         /* use player element that is initially defined in the level playfield,
14306            not the player element that corresponds to the runtime player number
14307            (example: a level that contains EL_PLAYER_3 as the only player would
14308            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14309         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14310
14311         CheckElementChangeBySide(x, y, hitting_element, player_element,
14312                                  CE_HITTING_X, touched_side);
14313       }
14314 #endif
14315     }
14316   }
14317
14318   /* "hitting something" is also true when hitting the playfield border */
14319   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14320                            CE_HITTING_SOMETHING, direction);
14321 }
14322
14323 #if 0
14324 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14325 {
14326   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14327   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14328   int hitx = x + dx, hity = y + dy;
14329   int hitting_element = Feld[x][y];
14330   int touched_element;
14331 #if 0
14332   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14333                         !IS_FREE(hitx, hity) &&
14334                         (!IS_MOVING(hitx, hity) ||
14335                          MovDir[hitx][hity] != direction ||
14336                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14337 #endif
14338
14339   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14340     return;
14341
14342 #if 0
14343   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14344     return;
14345 #endif
14346
14347   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14348                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14349
14350   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14351                            EP_CAN_SMASH_EVERYTHING, direction);
14352
14353   if (IN_LEV_FIELD(hitx, hity))
14354   {
14355     int opposite_direction = MV_DIR_OPPOSITE(direction);
14356     int hitting_side = direction;
14357     int touched_side = opposite_direction;
14358 #if 0
14359     int touched_element = MovingOrBlocked2Element(hitx, hity);
14360 #endif
14361 #if 1
14362     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14363                           MovDir[hitx][hity] != direction ||
14364                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14365
14366     object_hit = TRUE;
14367 #endif
14368
14369     if (object_hit)
14370     {
14371       int i;
14372
14373       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14374                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14375
14376       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14377                                CE_OTHER_IS_SMASHING, touched_side);
14378
14379       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14380                                CE_OTHER_GETS_SMASHED, hitting_side);
14381     }
14382   }
14383 }
14384 #endif
14385
14386 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14387 {
14388   int i, kill_x = -1, kill_y = -1;
14389
14390   int bad_element = -1;
14391   static int test_xy[4][2] =
14392   {
14393     { 0, -1 },
14394     { -1, 0 },
14395     { +1, 0 },
14396     { 0, +1 }
14397   };
14398   static int test_dir[4] =
14399   {
14400     MV_UP,
14401     MV_LEFT,
14402     MV_RIGHT,
14403     MV_DOWN
14404   };
14405
14406   for (i = 0; i < NUM_DIRECTIONS; i++)
14407   {
14408     int test_x, test_y, test_move_dir, test_element;
14409
14410     test_x = good_x + test_xy[i][0];
14411     test_y = good_y + test_xy[i][1];
14412
14413     if (!IN_LEV_FIELD(test_x, test_y))
14414       continue;
14415
14416     test_move_dir =
14417       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14418
14419     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14420
14421     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14422        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14423     */
14424     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14425         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14426     {
14427       kill_x = test_x;
14428       kill_y = test_y;
14429       bad_element = test_element;
14430
14431       break;
14432     }
14433   }
14434
14435   if (kill_x != -1 || kill_y != -1)
14436   {
14437     if (IS_PLAYER(good_x, good_y))
14438     {
14439       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14440
14441       if (player->shield_deadly_time_left > 0 &&
14442           !IS_INDESTRUCTIBLE(bad_element))
14443         Bang(kill_x, kill_y);
14444       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14445         KillPlayer(player);
14446     }
14447     else
14448       Bang(good_x, good_y);
14449   }
14450 }
14451
14452 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14453 {
14454   int i, kill_x = -1, kill_y = -1;
14455   int bad_element = Feld[bad_x][bad_y];
14456   static int test_xy[4][2] =
14457   {
14458     { 0, -1 },
14459     { -1, 0 },
14460     { +1, 0 },
14461     { 0, +1 }
14462   };
14463   static int touch_dir[4] =
14464   {
14465     MV_LEFT | MV_RIGHT,
14466     MV_UP   | MV_DOWN,
14467     MV_UP   | MV_DOWN,
14468     MV_LEFT | MV_RIGHT
14469   };
14470   static int test_dir[4] =
14471   {
14472     MV_UP,
14473     MV_LEFT,
14474     MV_RIGHT,
14475     MV_DOWN
14476   };
14477
14478   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14479     return;
14480
14481   for (i = 0; i < NUM_DIRECTIONS; i++)
14482   {
14483     int test_x, test_y, test_move_dir, test_element;
14484
14485     test_x = bad_x + test_xy[i][0];
14486     test_y = bad_y + test_xy[i][1];
14487
14488     if (!IN_LEV_FIELD(test_x, test_y))
14489       continue;
14490
14491     test_move_dir =
14492       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14493
14494     test_element = Feld[test_x][test_y];
14495
14496     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14497        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14498     */
14499     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14500         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14501     {
14502       /* good thing is player or penguin that does not move away */
14503       if (IS_PLAYER(test_x, test_y))
14504       {
14505         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14506
14507         if (bad_element == EL_ROBOT && player->is_moving)
14508           continue;     /* robot does not kill player if he is moving */
14509
14510         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14511         {
14512           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14513             continue;           /* center and border element do not touch */
14514         }
14515
14516         kill_x = test_x;
14517         kill_y = test_y;
14518
14519         break;
14520       }
14521       else if (test_element == EL_PENGUIN)
14522       {
14523         kill_x = test_x;
14524         kill_y = test_y;
14525
14526         break;
14527       }
14528     }
14529   }
14530
14531   if (kill_x != -1 || kill_y != -1)
14532   {
14533     if (IS_PLAYER(kill_x, kill_y))
14534     {
14535       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14536
14537       if (player->shield_deadly_time_left > 0 &&
14538           !IS_INDESTRUCTIBLE(bad_element))
14539         Bang(bad_x, bad_y);
14540       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14541         KillPlayer(player);
14542     }
14543     else
14544       Bang(kill_x, kill_y);
14545   }
14546 }
14547
14548 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14549 {
14550   int bad_element = Feld[bad_x][bad_y];
14551   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14552   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14553   int test_x = bad_x + dx, test_y = bad_y + dy;
14554   int test_move_dir, test_element;
14555   int kill_x = -1, kill_y = -1;
14556
14557   if (!IN_LEV_FIELD(test_x, test_y))
14558     return;
14559
14560   test_move_dir =
14561     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14562
14563   test_element = Feld[test_x][test_y];
14564
14565   if (test_move_dir != bad_move_dir)
14566   {
14567     /* good thing can be player or penguin that does not move away */
14568     if (IS_PLAYER(test_x, test_y))
14569     {
14570       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14571
14572       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14573          player as being hit when he is moving towards the bad thing, because
14574          the "get hit by" condition would be lost after the player stops) */
14575       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14576         return;         /* player moves away from bad thing */
14577
14578       kill_x = test_x;
14579       kill_y = test_y;
14580     }
14581     else if (test_element == EL_PENGUIN)
14582     {
14583       kill_x = test_x;
14584       kill_y = test_y;
14585     }
14586   }
14587
14588   if (kill_x != -1 || kill_y != -1)
14589   {
14590     if (IS_PLAYER(kill_x, kill_y))
14591     {
14592       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14593
14594       if (player->shield_deadly_time_left > 0 &&
14595           !IS_INDESTRUCTIBLE(bad_element))
14596         Bang(bad_x, bad_y);
14597       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14598         KillPlayer(player);
14599     }
14600     else
14601       Bang(kill_x, kill_y);
14602   }
14603 }
14604
14605 void TestIfPlayerTouchesBadThing(int x, int y)
14606 {
14607   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14608 }
14609
14610 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14611 {
14612   TestIfGoodThingHitsBadThing(x, y, move_dir);
14613 }
14614
14615 void TestIfBadThingTouchesPlayer(int x, int y)
14616 {
14617   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14618 }
14619
14620 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14621 {
14622   TestIfBadThingHitsGoodThing(x, y, move_dir);
14623 }
14624
14625 void TestIfFriendTouchesBadThing(int x, int y)
14626 {
14627   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14628 }
14629
14630 void TestIfBadThingTouchesFriend(int x, int y)
14631 {
14632   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14633 }
14634
14635 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14636 {
14637   int i, kill_x = bad_x, kill_y = bad_y;
14638   static int xy[4][2] =
14639   {
14640     { 0, -1 },
14641     { -1, 0 },
14642     { +1, 0 },
14643     { 0, +1 }
14644   };
14645
14646   for (i = 0; i < NUM_DIRECTIONS; i++)
14647   {
14648     int x, y, element;
14649
14650     x = bad_x + xy[i][0];
14651     y = bad_y + xy[i][1];
14652     if (!IN_LEV_FIELD(x, y))
14653       continue;
14654
14655     element = Feld[x][y];
14656     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14657         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14658     {
14659       kill_x = x;
14660       kill_y = y;
14661       break;
14662     }
14663   }
14664
14665   if (kill_x != bad_x || kill_y != bad_y)
14666     Bang(bad_x, bad_y);
14667 }
14668
14669 void KillPlayer(struct PlayerInfo *player)
14670 {
14671   int jx = player->jx, jy = player->jy;
14672
14673   if (!player->active)
14674     return;
14675
14676 #if 0
14677   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14678          player->killed, player->active, player->reanimated);
14679 #endif
14680
14681   /* the following code was introduced to prevent an infinite loop when calling
14682      -> Bang()
14683      -> CheckTriggeredElementChangeExt()
14684      -> ExecuteCustomElementAction()
14685      -> KillPlayer()
14686      -> (infinitely repeating the above sequence of function calls)
14687      which occurs when killing the player while having a CE with the setting
14688      "kill player X when explosion of <player X>"; the solution using a new
14689      field "player->killed" was chosen for backwards compatibility, although
14690      clever use of the fields "player->active" etc. would probably also work */
14691 #if 1
14692   if (player->killed)
14693     return;
14694 #endif
14695
14696   player->killed = TRUE;
14697
14698   /* remove accessible field at the player's position */
14699   Feld[jx][jy] = EL_EMPTY;
14700
14701   /* deactivate shield (else Bang()/Explode() would not work right) */
14702   player->shield_normal_time_left = 0;
14703   player->shield_deadly_time_left = 0;
14704
14705 #if 0
14706   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14707          player->killed, player->active, player->reanimated);
14708 #endif
14709
14710   Bang(jx, jy);
14711
14712 #if 0
14713   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14714          player->killed, player->active, player->reanimated);
14715 #endif
14716
14717 #if USE_PLAYER_REANIMATION
14718 #if 1
14719   if (player->reanimated)       /* killed player may have been reanimated */
14720     player->killed = player->reanimated = FALSE;
14721   else
14722     BuryPlayer(player);
14723 #else
14724   if (player->killed)           /* player may have been reanimated */
14725     BuryPlayer(player);
14726 #endif
14727 #else
14728   BuryPlayer(player);
14729 #endif
14730 }
14731
14732 static void KillPlayerUnlessEnemyProtected(int x, int y)
14733 {
14734   if (!PLAYER_ENEMY_PROTECTED(x, y))
14735     KillPlayer(PLAYERINFO(x, y));
14736 }
14737
14738 static void KillPlayerUnlessExplosionProtected(int x, int y)
14739 {
14740   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14741     KillPlayer(PLAYERINFO(x, y));
14742 }
14743
14744 void BuryPlayer(struct PlayerInfo *player)
14745 {
14746   int jx = player->jx, jy = player->jy;
14747
14748   if (!player->active)
14749     return;
14750
14751   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14752   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14753
14754   player->GameOver = TRUE;
14755   RemovePlayer(player);
14756 }
14757
14758 void RemovePlayer(struct PlayerInfo *player)
14759 {
14760   int jx = player->jx, jy = player->jy;
14761   int i, found = FALSE;
14762
14763   player->present = FALSE;
14764   player->active = FALSE;
14765
14766   if (!ExplodeField[jx][jy])
14767     StorePlayer[jx][jy] = 0;
14768
14769   if (player->is_moving)
14770     TEST_DrawLevelField(player->last_jx, player->last_jy);
14771
14772   for (i = 0; i < MAX_PLAYERS; i++)
14773     if (stored_player[i].active)
14774       found = TRUE;
14775
14776   if (!found)
14777     AllPlayersGone = TRUE;
14778
14779   ExitX = ZX = jx;
14780   ExitY = ZY = jy;
14781 }
14782
14783 #if USE_NEW_SNAP_DELAY
14784 static void setFieldForSnapping(int x, int y, int element, int direction)
14785 {
14786   struct ElementInfo *ei = &element_info[element];
14787   int direction_bit = MV_DIR_TO_BIT(direction);
14788   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14789   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14790                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14791
14792   Feld[x][y] = EL_ELEMENT_SNAPPING;
14793   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14794
14795   ResetGfxAnimation(x, y);
14796
14797   GfxElement[x][y] = element;
14798   GfxAction[x][y] = action;
14799   GfxDir[x][y] = direction;
14800   GfxFrame[x][y] = -1;
14801 }
14802 #endif
14803
14804 /*
14805   =============================================================================
14806   checkDiagonalPushing()
14807   -----------------------------------------------------------------------------
14808   check if diagonal input device direction results in pushing of object
14809   (by checking if the alternative direction is walkable, diggable, ...)
14810   =============================================================================
14811 */
14812
14813 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14814                                     int x, int y, int real_dx, int real_dy)
14815 {
14816   int jx, jy, dx, dy, xx, yy;
14817
14818   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14819     return TRUE;
14820
14821   /* diagonal direction: check alternative direction */
14822   jx = player->jx;
14823   jy = player->jy;
14824   dx = x - jx;
14825   dy = y - jy;
14826   xx = jx + (dx == 0 ? real_dx : 0);
14827   yy = jy + (dy == 0 ? real_dy : 0);
14828
14829   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14830 }
14831
14832 /*
14833   =============================================================================
14834   DigField()
14835   -----------------------------------------------------------------------------
14836   x, y:                 field next to player (non-diagonal) to try to dig to
14837   real_dx, real_dy:     direction as read from input device (can be diagonal)
14838   =============================================================================
14839 */
14840
14841 static int DigField(struct PlayerInfo *player,
14842                     int oldx, int oldy, int x, int y,
14843                     int real_dx, int real_dy, int mode)
14844 {
14845   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14846   boolean player_was_pushing = player->is_pushing;
14847   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14848   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14849   int jx = oldx, jy = oldy;
14850   int dx = x - jx, dy = y - jy;
14851   int nextx = x + dx, nexty = y + dy;
14852   int move_direction = (dx == -1 ? MV_LEFT  :
14853                         dx == +1 ? MV_RIGHT :
14854                         dy == -1 ? MV_UP    :
14855                         dy == +1 ? MV_DOWN  : MV_NONE);
14856   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14857   int dig_side = MV_DIR_OPPOSITE(move_direction);
14858   int old_element = Feld[jx][jy];
14859 #if USE_FIXED_DONT_RUN_INTO
14860   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14861 #else
14862   int element;
14863 #endif
14864   int collect_count;
14865
14866   if (is_player)                /* function can also be called by EL_PENGUIN */
14867   {
14868     if (player->MovPos == 0)
14869     {
14870       player->is_digging = FALSE;
14871       player->is_collecting = FALSE;
14872     }
14873
14874     if (player->MovPos == 0)    /* last pushing move finished */
14875       player->is_pushing = FALSE;
14876
14877     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14878     {
14879       player->is_switching = FALSE;
14880       player->push_delay = -1;
14881
14882       return MP_NO_ACTION;
14883     }
14884   }
14885
14886 #if !USE_FIXED_DONT_RUN_INTO
14887   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14888     return MP_NO_ACTION;
14889 #endif
14890
14891   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14892     old_element = Back[jx][jy];
14893
14894   /* in case of element dropped at player position, check background */
14895   else if (Back[jx][jy] != EL_EMPTY &&
14896            game.engine_version >= VERSION_IDENT(2,2,0,0))
14897     old_element = Back[jx][jy];
14898
14899   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14900     return MP_NO_ACTION;        /* field has no opening in this direction */
14901
14902   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14903     return MP_NO_ACTION;        /* field has no opening in this direction */
14904
14905 #if USE_FIXED_DONT_RUN_INTO
14906   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14907   {
14908     SplashAcid(x, y);
14909
14910     Feld[jx][jy] = player->artwork_element;
14911     InitMovingField(jx, jy, MV_DOWN);
14912     Store[jx][jy] = EL_ACID;
14913     ContinueMoving(jx, jy);
14914     BuryPlayer(player);
14915
14916     return MP_DONT_RUN_INTO;
14917   }
14918 #endif
14919
14920 #if USE_FIXED_DONT_RUN_INTO
14921   if (player_can_move && DONT_RUN_INTO(element))
14922   {
14923     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14924
14925     return MP_DONT_RUN_INTO;
14926   }
14927 #endif
14928
14929 #if USE_FIXED_DONT_RUN_INTO
14930   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14931     return MP_NO_ACTION;
14932 #endif
14933
14934 #if !USE_FIXED_DONT_RUN_INTO
14935   element = Feld[x][y];
14936 #endif
14937
14938   collect_count = element_info[element].collect_count_initial;
14939
14940   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14941     return MP_NO_ACTION;
14942
14943   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14944     player_can_move = player_can_move_or_snap;
14945
14946   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14947       game.engine_version >= VERSION_IDENT(2,2,0,0))
14948   {
14949     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14950                                player->index_bit, dig_side);
14951     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14952                                         player->index_bit, dig_side);
14953
14954     if (element == EL_DC_LANDMINE)
14955       Bang(x, y);
14956
14957     if (Feld[x][y] != element)          /* field changed by snapping */
14958       return MP_ACTION;
14959
14960     return MP_NO_ACTION;
14961   }
14962
14963 #if USE_PLAYER_GRAVITY
14964   if (player->gravity && is_player && !player->is_auto_moving &&
14965       canFallDown(player) && move_direction != MV_DOWN &&
14966       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14967     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14968 #else
14969   if (game.gravity && is_player && !player->is_auto_moving &&
14970       canFallDown(player) && move_direction != MV_DOWN &&
14971       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14972     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14973 #endif
14974
14975   if (player_can_move &&
14976       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14977   {
14978     int sound_element = SND_ELEMENT(element);
14979     int sound_action = ACTION_WALKING;
14980
14981     if (IS_RND_GATE(element))
14982     {
14983       if (!player->key[RND_GATE_NR(element)])
14984         return MP_NO_ACTION;
14985     }
14986     else if (IS_RND_GATE_GRAY(element))
14987     {
14988       if (!player->key[RND_GATE_GRAY_NR(element)])
14989         return MP_NO_ACTION;
14990     }
14991     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14992     {
14993       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14994         return MP_NO_ACTION;
14995     }
14996     else if (element == EL_EXIT_OPEN ||
14997              element == EL_EM_EXIT_OPEN ||
14998 #if 1
14999              element == EL_EM_EXIT_OPENING ||
15000 #endif
15001              element == EL_STEEL_EXIT_OPEN ||
15002              element == EL_EM_STEEL_EXIT_OPEN ||
15003 #if 1
15004              element == EL_EM_STEEL_EXIT_OPENING ||
15005 #endif
15006              element == EL_SP_EXIT_OPEN ||
15007              element == EL_SP_EXIT_OPENING)
15008     {
15009       sound_action = ACTION_PASSING;    /* player is passing exit */
15010     }
15011     else if (element == EL_EMPTY)
15012     {
15013       sound_action = ACTION_MOVING;             /* nothing to walk on */
15014     }
15015
15016     /* play sound from background or player, whatever is available */
15017     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15018       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15019     else
15020       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15021   }
15022   else if (player_can_move &&
15023            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15024   {
15025     if (!ACCESS_FROM(element, opposite_direction))
15026       return MP_NO_ACTION;      /* field not accessible from this direction */
15027
15028     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15029       return MP_NO_ACTION;
15030
15031     if (IS_EM_GATE(element))
15032     {
15033       if (!player->key[EM_GATE_NR(element)])
15034         return MP_NO_ACTION;
15035     }
15036     else if (IS_EM_GATE_GRAY(element))
15037     {
15038       if (!player->key[EM_GATE_GRAY_NR(element)])
15039         return MP_NO_ACTION;
15040     }
15041     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15042     {
15043       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15044         return MP_NO_ACTION;
15045     }
15046     else if (IS_EMC_GATE(element))
15047     {
15048       if (!player->key[EMC_GATE_NR(element)])
15049         return MP_NO_ACTION;
15050     }
15051     else if (IS_EMC_GATE_GRAY(element))
15052     {
15053       if (!player->key[EMC_GATE_GRAY_NR(element)])
15054         return MP_NO_ACTION;
15055     }
15056     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15057     {
15058       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15059         return MP_NO_ACTION;
15060     }
15061     else if (element == EL_DC_GATE_WHITE ||
15062              element == EL_DC_GATE_WHITE_GRAY ||
15063              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15064     {
15065       if (player->num_white_keys == 0)
15066         return MP_NO_ACTION;
15067
15068       player->num_white_keys--;
15069     }
15070     else if (IS_SP_PORT(element))
15071     {
15072       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15073           element == EL_SP_GRAVITY_PORT_RIGHT ||
15074           element == EL_SP_GRAVITY_PORT_UP ||
15075           element == EL_SP_GRAVITY_PORT_DOWN)
15076 #if USE_PLAYER_GRAVITY
15077         player->gravity = !player->gravity;
15078 #else
15079         game.gravity = !game.gravity;
15080 #endif
15081       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15082                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15083                element == EL_SP_GRAVITY_ON_PORT_UP ||
15084                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15085 #if USE_PLAYER_GRAVITY
15086         player->gravity = TRUE;
15087 #else
15088         game.gravity = TRUE;
15089 #endif
15090       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15091                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15092                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15093                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095         player->gravity = FALSE;
15096 #else
15097         game.gravity = FALSE;
15098 #endif
15099     }
15100
15101     /* automatically move to the next field with double speed */
15102     player->programmed_action = move_direction;
15103
15104     if (player->move_delay_reset_counter == 0)
15105     {
15106       player->move_delay_reset_counter = 2;     /* two double speed steps */
15107
15108       DOUBLE_PLAYER_SPEED(player);
15109     }
15110
15111     PlayLevelSoundAction(x, y, ACTION_PASSING);
15112   }
15113   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15114   {
15115     RemoveField(x, y);
15116
15117     if (mode != DF_SNAP)
15118     {
15119       GfxElement[x][y] = GFX_ELEMENT(element);
15120       player->is_digging = TRUE;
15121     }
15122
15123     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15124
15125     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15126                                         player->index_bit, dig_side);
15127
15128     if (mode == DF_SNAP)
15129     {
15130 #if USE_NEW_SNAP_DELAY
15131       if (level.block_snap_field)
15132         setFieldForSnapping(x, y, element, move_direction);
15133       else
15134         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15135 #else
15136       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15137 #endif
15138
15139       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15140                                           player->index_bit, dig_side);
15141     }
15142   }
15143   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15144   {
15145     RemoveField(x, y);
15146
15147     if (is_player && mode != DF_SNAP)
15148     {
15149       GfxElement[x][y] = element;
15150       player->is_collecting = TRUE;
15151     }
15152
15153     if (element == EL_SPEED_PILL)
15154     {
15155       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15156     }
15157     else if (element == EL_EXTRA_TIME && level.time > 0)
15158     {
15159       TimeLeft += level.extra_time;
15160
15161 #if 1
15162       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15163
15164       DisplayGameControlValues();
15165 #else
15166       DrawGameValue_Time(TimeLeft);
15167 #endif
15168     }
15169     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15170     {
15171       player->shield_normal_time_left += level.shield_normal_time;
15172       if (element == EL_SHIELD_DEADLY)
15173         player->shield_deadly_time_left += level.shield_deadly_time;
15174     }
15175     else if (element == EL_DYNAMITE ||
15176              element == EL_EM_DYNAMITE ||
15177              element == EL_SP_DISK_RED)
15178     {
15179       if (player->inventory_size < MAX_INVENTORY_SIZE)
15180         player->inventory_element[player->inventory_size++] = element;
15181
15182       DrawGameDoorValues();
15183     }
15184     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15185     {
15186       player->dynabomb_count++;
15187       player->dynabombs_left++;
15188     }
15189     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15190     {
15191       player->dynabomb_size++;
15192     }
15193     else if (element == EL_DYNABOMB_INCREASE_POWER)
15194     {
15195       player->dynabomb_xl = TRUE;
15196     }
15197     else if (IS_KEY(element))
15198     {
15199       player->key[KEY_NR(element)] = TRUE;
15200
15201       DrawGameDoorValues();
15202     }
15203     else if (element == EL_DC_KEY_WHITE)
15204     {
15205       player->num_white_keys++;
15206
15207       /* display white keys? */
15208       /* DrawGameDoorValues(); */
15209     }
15210     else if (IS_ENVELOPE(element))
15211     {
15212       player->show_envelope = element;
15213     }
15214     else if (element == EL_EMC_LENSES)
15215     {
15216       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15217
15218       RedrawAllInvisibleElementsForLenses();
15219     }
15220     else if (element == EL_EMC_MAGNIFIER)
15221     {
15222       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15223
15224       RedrawAllInvisibleElementsForMagnifier();
15225     }
15226     else if (IS_DROPPABLE(element) ||
15227              IS_THROWABLE(element))     /* can be collected and dropped */
15228     {
15229       int i;
15230
15231       if (collect_count == 0)
15232         player->inventory_infinite_element = element;
15233       else
15234         for (i = 0; i < collect_count; i++)
15235           if (player->inventory_size < MAX_INVENTORY_SIZE)
15236             player->inventory_element[player->inventory_size++] = element;
15237
15238       DrawGameDoorValues();
15239     }
15240     else if (collect_count > 0)
15241     {
15242       local_player->gems_still_needed -= collect_count;
15243       if (local_player->gems_still_needed < 0)
15244         local_player->gems_still_needed = 0;
15245
15246 #if 1
15247       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15248
15249       DisplayGameControlValues();
15250 #else
15251       DrawGameValue_Emeralds(local_player->gems_still_needed);
15252 #endif
15253     }
15254
15255     RaiseScoreElement(element);
15256     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15257
15258     if (is_player)
15259       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15260                                           player->index_bit, dig_side);
15261
15262     if (mode == DF_SNAP)
15263     {
15264 #if USE_NEW_SNAP_DELAY
15265       if (level.block_snap_field)
15266         setFieldForSnapping(x, y, element, move_direction);
15267       else
15268         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15269 #else
15270       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15271 #endif
15272
15273       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15274                                           player->index_bit, dig_side);
15275     }
15276   }
15277   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15278   {
15279     if (mode == DF_SNAP && element != EL_BD_ROCK)
15280       return MP_NO_ACTION;
15281
15282     if (CAN_FALL(element) && dy)
15283       return MP_NO_ACTION;
15284
15285     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15286         !(element == EL_SPRING && level.use_spring_bug))
15287       return MP_NO_ACTION;
15288
15289     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15290         ((move_direction & MV_VERTICAL &&
15291           ((element_info[element].move_pattern & MV_LEFT &&
15292             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15293            (element_info[element].move_pattern & MV_RIGHT &&
15294             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15295          (move_direction & MV_HORIZONTAL &&
15296           ((element_info[element].move_pattern & MV_UP &&
15297             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15298            (element_info[element].move_pattern & MV_DOWN &&
15299             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15300       return MP_NO_ACTION;
15301
15302     /* do not push elements already moving away faster than player */
15303     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15304         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15305       return MP_NO_ACTION;
15306
15307     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15308     {
15309       if (player->push_delay_value == -1 || !player_was_pushing)
15310         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15311     }
15312     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15313     {
15314       if (player->push_delay_value == -1)
15315         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15316     }
15317     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15318     {
15319       if (!player->is_pushing)
15320         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15321     }
15322
15323     player->is_pushing = TRUE;
15324     player->is_active = TRUE;
15325
15326     if (!(IN_LEV_FIELD(nextx, nexty) &&
15327           (IS_FREE(nextx, nexty) ||
15328            (IS_SB_ELEMENT(element) &&
15329             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15330            (IS_CUSTOM_ELEMENT(element) &&
15331             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15332       return MP_NO_ACTION;
15333
15334     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15335       return MP_NO_ACTION;
15336
15337     if (player->push_delay == -1)       /* new pushing; restart delay */
15338       player->push_delay = 0;
15339
15340     if (player->push_delay < player->push_delay_value &&
15341         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15342         element != EL_SPRING && element != EL_BALLOON)
15343     {
15344       /* make sure that there is no move delay before next try to push */
15345       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15346         player->move_delay = 0;
15347
15348       return MP_NO_ACTION;
15349     }
15350
15351     if (IS_CUSTOM_ELEMENT(element) &&
15352         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15353     {
15354       if (!DigFieldByCE(nextx, nexty, element))
15355         return MP_NO_ACTION;
15356     }
15357
15358     if (IS_SB_ELEMENT(element))
15359     {
15360       if (element == EL_SOKOBAN_FIELD_FULL)
15361       {
15362         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15363         local_player->sokobanfields_still_needed++;
15364       }
15365
15366       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15367       {
15368         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15369         local_player->sokobanfields_still_needed--;
15370       }
15371
15372       Feld[x][y] = EL_SOKOBAN_OBJECT;
15373
15374       if (Back[x][y] == Back[nextx][nexty])
15375         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15376       else if (Back[x][y] != 0)
15377         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15378                                     ACTION_EMPTYING);
15379       else
15380         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15381                                     ACTION_FILLING);
15382
15383       if (local_player->sokobanfields_still_needed == 0 &&
15384           game.emulation == EMU_SOKOBAN)
15385       {
15386         PlayerWins(player);
15387
15388         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15389       }
15390     }
15391     else
15392       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15393
15394     InitMovingField(x, y, move_direction);
15395     GfxAction[x][y] = ACTION_PUSHING;
15396
15397     if (mode == DF_SNAP)
15398       ContinueMoving(x, y);
15399     else
15400       MovPos[x][y] = (dx != 0 ? dx : dy);
15401
15402     Pushed[x][y] = TRUE;
15403     Pushed[nextx][nexty] = TRUE;
15404
15405     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15406       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15407     else
15408       player->push_delay_value = -1;    /* get new value later */
15409
15410     /* check for element change _after_ element has been pushed */
15411     if (game.use_change_when_pushing_bug)
15412     {
15413       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15414                                  player->index_bit, dig_side);
15415       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15416                                           player->index_bit, dig_side);
15417     }
15418   }
15419   else if (IS_SWITCHABLE(element))
15420   {
15421     if (PLAYER_SWITCHING(player, x, y))
15422     {
15423       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15424                                           player->index_bit, dig_side);
15425
15426       return MP_ACTION;
15427     }
15428
15429     player->is_switching = TRUE;
15430     player->switch_x = x;
15431     player->switch_y = y;
15432
15433     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15434
15435     if (element == EL_ROBOT_WHEEL)
15436     {
15437       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15438       ZX = x;
15439       ZY = y;
15440
15441       game.robot_wheel_active = TRUE;
15442
15443       TEST_DrawLevelField(x, y);
15444     }
15445     else if (element == EL_SP_TERMINAL)
15446     {
15447       int xx, yy;
15448
15449       SCAN_PLAYFIELD(xx, yy)
15450       {
15451         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15452           Bang(xx, yy);
15453         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15454           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15455       }
15456     }
15457     else if (IS_BELT_SWITCH(element))
15458     {
15459       ToggleBeltSwitch(x, y);
15460     }
15461     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15462              element == EL_SWITCHGATE_SWITCH_DOWN ||
15463              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15464              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15465     {
15466       ToggleSwitchgateSwitch(x, y);
15467     }
15468     else if (element == EL_LIGHT_SWITCH ||
15469              element == EL_LIGHT_SWITCH_ACTIVE)
15470     {
15471       ToggleLightSwitch(x, y);
15472     }
15473     else if (element == EL_TIMEGATE_SWITCH ||
15474              element == EL_DC_TIMEGATE_SWITCH)
15475     {
15476       ActivateTimegateSwitch(x, y);
15477     }
15478     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15479              element == EL_BALLOON_SWITCH_RIGHT ||
15480              element == EL_BALLOON_SWITCH_UP    ||
15481              element == EL_BALLOON_SWITCH_DOWN  ||
15482              element == EL_BALLOON_SWITCH_NONE  ||
15483              element == EL_BALLOON_SWITCH_ANY)
15484     {
15485       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15486                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15487                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15488                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15489                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15490                              move_direction);
15491     }
15492     else if (element == EL_LAMP)
15493     {
15494       Feld[x][y] = EL_LAMP_ACTIVE;
15495       local_player->lights_still_needed--;
15496
15497       ResetGfxAnimation(x, y);
15498       TEST_DrawLevelField(x, y);
15499     }
15500     else if (element == EL_TIME_ORB_FULL)
15501     {
15502       Feld[x][y] = EL_TIME_ORB_EMPTY;
15503
15504       if (level.time > 0 || level.use_time_orb_bug)
15505       {
15506         TimeLeft += level.time_orb_time;
15507
15508 #if 1
15509         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15510
15511         DisplayGameControlValues();
15512 #else
15513         DrawGameValue_Time(TimeLeft);
15514 #endif
15515       }
15516
15517       ResetGfxAnimation(x, y);
15518       TEST_DrawLevelField(x, y);
15519     }
15520     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15521              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15522     {
15523       int xx, yy;
15524
15525       game.ball_state = !game.ball_state;
15526
15527       SCAN_PLAYFIELD(xx, yy)
15528       {
15529         int e = Feld[xx][yy];
15530
15531         if (game.ball_state)
15532         {
15533           if (e == EL_EMC_MAGIC_BALL)
15534             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15535           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15536             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15537         }
15538         else
15539         {
15540           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15541             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15542           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15543             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15544         }
15545       }
15546     }
15547
15548     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15549                                         player->index_bit, dig_side);
15550
15551     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15552                                         player->index_bit, dig_side);
15553
15554     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15555                                         player->index_bit, dig_side);
15556
15557     return MP_ACTION;
15558   }
15559   else
15560   {
15561     if (!PLAYER_SWITCHING(player, x, y))
15562     {
15563       player->is_switching = TRUE;
15564       player->switch_x = x;
15565       player->switch_y = y;
15566
15567       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15568                                  player->index_bit, dig_side);
15569       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15570                                           player->index_bit, dig_side);
15571
15572       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15573                                  player->index_bit, dig_side);
15574       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15575                                           player->index_bit, dig_side);
15576     }
15577
15578     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15579                                player->index_bit, dig_side);
15580     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15581                                         player->index_bit, dig_side);
15582
15583     return MP_NO_ACTION;
15584   }
15585
15586   player->push_delay = -1;
15587
15588   if (is_player)                /* function can also be called by EL_PENGUIN */
15589   {
15590     if (Feld[x][y] != element)          /* really digged/collected something */
15591     {
15592       player->is_collecting = !player->is_digging;
15593       player->is_active = TRUE;
15594     }
15595   }
15596
15597   return MP_MOVING;
15598 }
15599
15600 static boolean DigFieldByCE(int x, int y, int digging_element)
15601 {
15602   int element = Feld[x][y];
15603
15604   if (!IS_FREE(x, y))
15605   {
15606     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15607                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15608                   ACTION_BREAKING);
15609
15610     /* no element can dig solid indestructible elements */
15611     if (IS_INDESTRUCTIBLE(element) &&
15612         !IS_DIGGABLE(element) &&
15613         !IS_COLLECTIBLE(element))
15614       return FALSE;
15615
15616     if (AmoebaNr[x][y] &&
15617         (element == EL_AMOEBA_FULL ||
15618          element == EL_BD_AMOEBA ||
15619          element == EL_AMOEBA_GROWING))
15620     {
15621       AmoebaCnt[AmoebaNr[x][y]]--;
15622       AmoebaCnt2[AmoebaNr[x][y]]--;
15623     }
15624
15625     if (IS_MOVING(x, y))
15626       RemoveMovingField(x, y);
15627     else
15628     {
15629       RemoveField(x, y);
15630       TEST_DrawLevelField(x, y);
15631     }
15632
15633     /* if digged element was about to explode, prevent the explosion */
15634     ExplodeField[x][y] = EX_TYPE_NONE;
15635
15636     PlayLevelSoundAction(x, y, action);
15637   }
15638
15639   Store[x][y] = EL_EMPTY;
15640
15641 #if 1
15642   /* this makes it possible to leave the removed element again */
15643   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15644     Store[x][y] = element;
15645 #else
15646   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15647   {
15648     int move_leave_element = element_info[digging_element].move_leave_element;
15649
15650     /* this makes it possible to leave the removed element again */
15651     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15652                    element : move_leave_element);
15653   }
15654 #endif
15655
15656   return TRUE;
15657 }
15658
15659 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15660 {
15661   int jx = player->jx, jy = player->jy;
15662   int x = jx + dx, y = jy + dy;
15663   int snap_direction = (dx == -1 ? MV_LEFT  :
15664                         dx == +1 ? MV_RIGHT :
15665                         dy == -1 ? MV_UP    :
15666                         dy == +1 ? MV_DOWN  : MV_NONE);
15667   boolean can_continue_snapping = (level.continuous_snapping &&
15668                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15669
15670   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15671     return FALSE;
15672
15673   if (!player->active || !IN_LEV_FIELD(x, y))
15674     return FALSE;
15675
15676   if (dx && dy)
15677     return FALSE;
15678
15679   if (!dx && !dy)
15680   {
15681     if (player->MovPos == 0)
15682       player->is_pushing = FALSE;
15683
15684     player->is_snapping = FALSE;
15685
15686     if (player->MovPos == 0)
15687     {
15688       player->is_moving = FALSE;
15689       player->is_digging = FALSE;
15690       player->is_collecting = FALSE;
15691     }
15692
15693     return FALSE;
15694   }
15695
15696 #if USE_NEW_CONTINUOUS_SNAPPING
15697   /* prevent snapping with already pressed snap key when not allowed */
15698   if (player->is_snapping && !can_continue_snapping)
15699     return FALSE;
15700 #else
15701   if (player->is_snapping)
15702     return FALSE;
15703 #endif
15704
15705   player->MovDir = snap_direction;
15706
15707   if (player->MovPos == 0)
15708   {
15709     player->is_moving = FALSE;
15710     player->is_digging = FALSE;
15711     player->is_collecting = FALSE;
15712   }
15713
15714   player->is_dropping = FALSE;
15715   player->is_dropping_pressed = FALSE;
15716   player->drop_pressed_delay = 0;
15717
15718   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15719     return FALSE;
15720
15721   player->is_snapping = TRUE;
15722   player->is_active = TRUE;
15723
15724   if (player->MovPos == 0)
15725   {
15726     player->is_moving = FALSE;
15727     player->is_digging = FALSE;
15728     player->is_collecting = FALSE;
15729   }
15730
15731   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15732     TEST_DrawLevelField(player->last_jx, player->last_jy);
15733
15734   TEST_DrawLevelField(x, y);
15735
15736   return TRUE;
15737 }
15738
15739 static boolean DropElement(struct PlayerInfo *player)
15740 {
15741   int old_element, new_element;
15742   int dropx = player->jx, dropy = player->jy;
15743   int drop_direction = player->MovDir;
15744   int drop_side = drop_direction;
15745 #if 1
15746   int drop_element = get_next_dropped_element(player);
15747 #else
15748   int drop_element = (player->inventory_size > 0 ?
15749                       player->inventory_element[player->inventory_size - 1] :
15750                       player->inventory_infinite_element != EL_UNDEFINED ?
15751                       player->inventory_infinite_element :
15752                       player->dynabombs_left > 0 ?
15753                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15754                       EL_UNDEFINED);
15755 #endif
15756
15757   player->is_dropping_pressed = TRUE;
15758
15759   /* do not drop an element on top of another element; when holding drop key
15760      pressed without moving, dropped element must move away before the next
15761      element can be dropped (this is especially important if the next element
15762      is dynamite, which can be placed on background for historical reasons) */
15763   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15764     return MP_ACTION;
15765
15766   if (IS_THROWABLE(drop_element))
15767   {
15768     dropx += GET_DX_FROM_DIR(drop_direction);
15769     dropy += GET_DY_FROM_DIR(drop_direction);
15770
15771     if (!IN_LEV_FIELD(dropx, dropy))
15772       return FALSE;
15773   }
15774
15775   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15776   new_element = drop_element;           /* default: no change when dropping */
15777
15778   /* check if player is active, not moving and ready to drop */
15779   if (!player->active || player->MovPos || player->drop_delay > 0)
15780     return FALSE;
15781
15782   /* check if player has anything that can be dropped */
15783   if (new_element == EL_UNDEFINED)
15784     return FALSE;
15785
15786   /* check if drop key was pressed long enough for EM style dynamite */
15787   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15788     return FALSE;
15789
15790   /* check if anything can be dropped at the current position */
15791   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15792     return FALSE;
15793
15794   /* collected custom elements can only be dropped on empty fields */
15795   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15796     return FALSE;
15797
15798   if (old_element != EL_EMPTY)
15799     Back[dropx][dropy] = old_element;   /* store old element on this field */
15800
15801   ResetGfxAnimation(dropx, dropy);
15802   ResetRandomAnimationValue(dropx, dropy);
15803
15804   if (player->inventory_size > 0 ||
15805       player->inventory_infinite_element != EL_UNDEFINED)
15806   {
15807     if (player->inventory_size > 0)
15808     {
15809       player->inventory_size--;
15810
15811       DrawGameDoorValues();
15812
15813       if (new_element == EL_DYNAMITE)
15814         new_element = EL_DYNAMITE_ACTIVE;
15815       else if (new_element == EL_EM_DYNAMITE)
15816         new_element = EL_EM_DYNAMITE_ACTIVE;
15817       else if (new_element == EL_SP_DISK_RED)
15818         new_element = EL_SP_DISK_RED_ACTIVE;
15819     }
15820
15821     Feld[dropx][dropy] = new_element;
15822
15823     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15824       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15825                           el2img(Feld[dropx][dropy]), 0);
15826
15827     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15828
15829     /* needed if previous element just changed to "empty" in the last frame */
15830     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15831
15832     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15833                                player->index_bit, drop_side);
15834     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15835                                         CE_PLAYER_DROPS_X,
15836                                         player->index_bit, drop_side);
15837
15838     TestIfElementTouchesCustomElement(dropx, dropy);
15839   }
15840   else          /* player is dropping a dyna bomb */
15841   {
15842     player->dynabombs_left--;
15843
15844     Feld[dropx][dropy] = new_element;
15845
15846     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15847       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15848                           el2img(Feld[dropx][dropy]), 0);
15849
15850     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15851   }
15852
15853   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15854     InitField_WithBug1(dropx, dropy, FALSE);
15855
15856   new_element = Feld[dropx][dropy];     /* element might have changed */
15857
15858   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15859       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15860   {
15861     int move_direction, nextx, nexty;
15862
15863     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15864       MovDir[dropx][dropy] = drop_direction;
15865
15866     move_direction = MovDir[dropx][dropy];
15867     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15868     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15869
15870     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15871
15872 #if USE_FIX_IMPACT_COLLISION
15873     /* do not cause impact style collision by dropping elements that can fall */
15874     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15875 #else
15876     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15877 #endif
15878   }
15879
15880   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15881   player->is_dropping = TRUE;
15882
15883   player->drop_pressed_delay = 0;
15884   player->is_dropping_pressed = FALSE;
15885
15886   player->drop_x = dropx;
15887   player->drop_y = dropy;
15888
15889   return TRUE;
15890 }
15891
15892 /* ------------------------------------------------------------------------- */
15893 /* game sound playing functions                                              */
15894 /* ------------------------------------------------------------------------- */
15895
15896 static int *loop_sound_frame = NULL;
15897 static int *loop_sound_volume = NULL;
15898
15899 void InitPlayLevelSound()
15900 {
15901   int num_sounds = getSoundListSize();
15902
15903   checked_free(loop_sound_frame);
15904   checked_free(loop_sound_volume);
15905
15906   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15907   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15908 }
15909
15910 static void PlayLevelSound(int x, int y, int nr)
15911 {
15912   int sx = SCREENX(x), sy = SCREENY(y);
15913   int volume, stereo_position;
15914   int max_distance = 8;
15915   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15916
15917   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15918       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15919     return;
15920
15921   if (!IN_LEV_FIELD(x, y) ||
15922       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15923       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15924     return;
15925
15926   volume = SOUND_MAX_VOLUME;
15927
15928   if (!IN_SCR_FIELD(sx, sy))
15929   {
15930     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15931     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15932
15933     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15934   }
15935
15936   stereo_position = (SOUND_MAX_LEFT +
15937                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15938                      (SCR_FIELDX + 2 * max_distance));
15939
15940   if (IS_LOOP_SOUND(nr))
15941   {
15942     /* This assures that quieter loop sounds do not overwrite louder ones,
15943        while restarting sound volume comparison with each new game frame. */
15944
15945     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15946       return;
15947
15948     loop_sound_volume[nr] = volume;
15949     loop_sound_frame[nr] = FrameCounter;
15950   }
15951
15952   PlaySoundExt(nr, volume, stereo_position, type);
15953 }
15954
15955 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15956 {
15957   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15958                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15959                  y < LEVELY(BY1) ? LEVELY(BY1) :
15960                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15961                  sound_action);
15962 }
15963
15964 static void PlayLevelSoundAction(int x, int y, int action)
15965 {
15966   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15967 }
15968
15969 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15970 {
15971   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15972
15973   if (sound_effect != SND_UNDEFINED)
15974     PlayLevelSound(x, y, sound_effect);
15975 }
15976
15977 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15978                                               int action)
15979 {
15980   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15981
15982   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15983     PlayLevelSound(x, y, sound_effect);
15984 }
15985
15986 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15987 {
15988   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15989
15990   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15991     PlayLevelSound(x, y, sound_effect);
15992 }
15993
15994 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15995 {
15996   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15997
15998   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15999     StopSound(sound_effect);
16000 }
16001
16002 static void PlayLevelMusic()
16003 {
16004   if (levelset.music[level_nr] != MUS_UNDEFINED)
16005     PlayMusic(levelset.music[level_nr]);        /* from config file */
16006   else
16007     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16008 }
16009
16010 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16011 {
16012   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16013   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16014   int x = xx - 1 - offset;
16015   int y = yy - 1 - offset;
16016
16017   switch (sample)
16018   {
16019     case SAMPLE_blank:
16020       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16021       break;
16022
16023     case SAMPLE_roll:
16024       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16025       break;
16026
16027     case SAMPLE_stone:
16028       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16029       break;
16030
16031     case SAMPLE_nut:
16032       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16033       break;
16034
16035     case SAMPLE_crack:
16036       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16037       break;
16038
16039     case SAMPLE_bug:
16040       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16041       break;
16042
16043     case SAMPLE_tank:
16044       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16045       break;
16046
16047     case SAMPLE_android_clone:
16048       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16049       break;
16050
16051     case SAMPLE_android_move:
16052       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16053       break;
16054
16055     case SAMPLE_spring:
16056       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16057       break;
16058
16059     case SAMPLE_slurp:
16060       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16061       break;
16062
16063     case SAMPLE_eater:
16064       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16065       break;
16066
16067     case SAMPLE_eater_eat:
16068       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16069       break;
16070
16071     case SAMPLE_alien:
16072       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16073       break;
16074
16075     case SAMPLE_collect:
16076       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16077       break;
16078
16079     case SAMPLE_diamond:
16080       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16081       break;
16082
16083     case SAMPLE_squash:
16084       /* !!! CHECK THIS !!! */
16085 #if 1
16086       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16087 #else
16088       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16089 #endif
16090       break;
16091
16092     case SAMPLE_wonderfall:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16094       break;
16095
16096     case SAMPLE_drip:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16098       break;
16099
16100     case SAMPLE_push:
16101       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16102       break;
16103
16104     case SAMPLE_dirt:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16106       break;
16107
16108     case SAMPLE_acid:
16109       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16110       break;
16111
16112     case SAMPLE_ball:
16113       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16114       break;
16115
16116     case SAMPLE_grow:
16117       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16118       break;
16119
16120     case SAMPLE_wonder:
16121       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16122       break;
16123
16124     case SAMPLE_door:
16125       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16126       break;
16127
16128     case SAMPLE_exit_open:
16129       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16130       break;
16131
16132     case SAMPLE_exit_leave:
16133       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16134       break;
16135
16136     case SAMPLE_dynamite:
16137       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16138       break;
16139
16140     case SAMPLE_tick:
16141       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16142       break;
16143
16144     case SAMPLE_press:
16145       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16146       break;
16147
16148     case SAMPLE_wheel:
16149       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16150       break;
16151
16152     case SAMPLE_boom:
16153       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16154       break;
16155
16156     case SAMPLE_die:
16157       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16158       break;
16159
16160     case SAMPLE_time:
16161       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16162       break;
16163
16164     default:
16165       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16166       break;
16167   }
16168 }
16169
16170 #if 0
16171 void ChangeTime(int value)
16172 {
16173   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16174
16175   *time += value;
16176
16177   /* EMC game engine uses value from time counter of RND game engine */
16178   level.native_em_level->lev->time = *time;
16179
16180   DrawGameValue_Time(*time);
16181 }
16182
16183 void RaiseScore(int value)
16184 {
16185   /* EMC game engine and RND game engine have separate score counters */
16186   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16187                 &level.native_em_level->lev->score : &local_player->score);
16188
16189   *score += value;
16190
16191   DrawGameValue_Score(*score);
16192 }
16193 #endif
16194
16195 void RaiseScore(int value)
16196 {
16197   local_player->score += value;
16198
16199 #if 1
16200   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16201
16202   DisplayGameControlValues();
16203 #else
16204   DrawGameValue_Score(local_player->score);
16205 #endif
16206 }
16207
16208 void RaiseScoreElement(int element)
16209 {
16210   switch (element)
16211   {
16212     case EL_EMERALD:
16213     case EL_BD_DIAMOND:
16214     case EL_EMERALD_YELLOW:
16215     case EL_EMERALD_RED:
16216     case EL_EMERALD_PURPLE:
16217     case EL_SP_INFOTRON:
16218       RaiseScore(level.score[SC_EMERALD]);
16219       break;
16220     case EL_DIAMOND:
16221       RaiseScore(level.score[SC_DIAMOND]);
16222       break;
16223     case EL_CRYSTAL:
16224       RaiseScore(level.score[SC_CRYSTAL]);
16225       break;
16226     case EL_PEARL:
16227       RaiseScore(level.score[SC_PEARL]);
16228       break;
16229     case EL_BUG:
16230     case EL_BD_BUTTERFLY:
16231     case EL_SP_ELECTRON:
16232       RaiseScore(level.score[SC_BUG]);
16233       break;
16234     case EL_SPACESHIP:
16235     case EL_BD_FIREFLY:
16236     case EL_SP_SNIKSNAK:
16237       RaiseScore(level.score[SC_SPACESHIP]);
16238       break;
16239     case EL_YAMYAM:
16240     case EL_DARK_YAMYAM:
16241       RaiseScore(level.score[SC_YAMYAM]);
16242       break;
16243     case EL_ROBOT:
16244       RaiseScore(level.score[SC_ROBOT]);
16245       break;
16246     case EL_PACMAN:
16247       RaiseScore(level.score[SC_PACMAN]);
16248       break;
16249     case EL_NUT:
16250       RaiseScore(level.score[SC_NUT]);
16251       break;
16252     case EL_DYNAMITE:
16253     case EL_EM_DYNAMITE:
16254     case EL_SP_DISK_RED:
16255     case EL_DYNABOMB_INCREASE_NUMBER:
16256     case EL_DYNABOMB_INCREASE_SIZE:
16257     case EL_DYNABOMB_INCREASE_POWER:
16258       RaiseScore(level.score[SC_DYNAMITE]);
16259       break;
16260     case EL_SHIELD_NORMAL:
16261     case EL_SHIELD_DEADLY:
16262       RaiseScore(level.score[SC_SHIELD]);
16263       break;
16264     case EL_EXTRA_TIME:
16265       RaiseScore(level.extra_time_score);
16266       break;
16267     case EL_KEY_1:
16268     case EL_KEY_2:
16269     case EL_KEY_3:
16270     case EL_KEY_4:
16271     case EL_EM_KEY_1:
16272     case EL_EM_KEY_2:
16273     case EL_EM_KEY_3:
16274     case EL_EM_KEY_4:
16275     case EL_EMC_KEY_5:
16276     case EL_EMC_KEY_6:
16277     case EL_EMC_KEY_7:
16278     case EL_EMC_KEY_8:
16279     case EL_DC_KEY_WHITE:
16280       RaiseScore(level.score[SC_KEY]);
16281       break;
16282     default:
16283       RaiseScore(element_info[element].collect_score);
16284       break;
16285   }
16286 }
16287
16288 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16289 {
16290   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16291   {
16292 #if defined(NETWORK_AVALIABLE)
16293     if (options.network)
16294       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16295     else
16296 #endif
16297     {
16298       if (quick_quit)
16299       {
16300 #if 1
16301
16302 #if 1
16303         FadeSkipNextFadeIn();
16304 #else
16305         fading = fading_none;
16306 #endif
16307
16308 #else
16309         OpenDoor(DOOR_CLOSE_1);
16310 #endif
16311
16312         game_status = GAME_MODE_MAIN;
16313
16314 #if 1
16315         DrawAndFadeInMainMenu(REDRAW_FIELD);
16316 #else
16317         DrawMainMenu();
16318 #endif
16319       }
16320       else
16321       {
16322 #if 0
16323         FadeOut(REDRAW_FIELD);
16324 #endif
16325
16326         game_status = GAME_MODE_MAIN;
16327
16328         DrawAndFadeInMainMenu(REDRAW_FIELD);
16329       }
16330     }
16331   }
16332   else          /* continue playing the game */
16333   {
16334     if (tape.playing && tape.deactivate_display)
16335       TapeDeactivateDisplayOff(TRUE);
16336
16337     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16338
16339     if (tape.playing && tape.deactivate_display)
16340       TapeDeactivateDisplayOn();
16341   }
16342 }
16343
16344 void RequestQuitGame(boolean ask_if_really_quit)
16345 {
16346   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16347   boolean skip_request = AllPlayersGone || quick_quit;
16348
16349   RequestQuitGameExt(skip_request, quick_quit,
16350                      "Do you really want to quit the game ?");
16351 }
16352
16353
16354 /* ------------------------------------------------------------------------- */
16355 /* random generator functions                                                */
16356 /* ------------------------------------------------------------------------- */
16357
16358 unsigned int InitEngineRandom_RND(long seed)
16359 {
16360   game.num_random_calls = 0;
16361
16362 #if 0
16363   unsigned int rnd_seed = InitEngineRandom(seed);
16364
16365   printf("::: START RND: %d\n", rnd_seed);
16366
16367   return rnd_seed;
16368 #else
16369
16370   return InitEngineRandom(seed);
16371
16372 #endif
16373
16374 }
16375
16376 unsigned int RND(int max)
16377 {
16378   if (max > 0)
16379   {
16380     game.num_random_calls++;
16381
16382     return GetEngineRandom(max);
16383   }
16384
16385   return 0;
16386 }
16387
16388
16389 /* ------------------------------------------------------------------------- */
16390 /* game engine snapshot handling functions                                   */
16391 /* ------------------------------------------------------------------------- */
16392
16393 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16394
16395 struct EngineSnapshotInfo
16396 {
16397   /* runtime values for custom element collect score */
16398   int collect_score[NUM_CUSTOM_ELEMENTS];
16399
16400   /* runtime values for group element choice position */
16401   int choice_pos[NUM_GROUP_ELEMENTS];
16402
16403   /* runtime values for belt position animations */
16404   int belt_graphic[4 * NUM_BELT_PARTS];
16405   int belt_anim_mode[4 * NUM_BELT_PARTS];
16406 };
16407
16408 struct EngineSnapshotNodeInfo
16409 {
16410   void *buffer_orig;
16411   void *buffer_copy;
16412   int size;
16413 };
16414
16415 static struct EngineSnapshotInfo engine_snapshot_rnd;
16416 static ListNode *engine_snapshot_list = NULL;
16417 static char *snapshot_level_identifier = NULL;
16418 static int snapshot_level_nr = -1;
16419
16420 void FreeEngineSnapshot()
16421 {
16422   while (engine_snapshot_list != NULL)
16423     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16424                        checked_free);
16425
16426   setString(&snapshot_level_identifier, NULL);
16427   snapshot_level_nr = -1;
16428 }
16429
16430 static void SaveEngineSnapshotValues_RND()
16431 {
16432   static int belt_base_active_element[4] =
16433   {
16434     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16435     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16436     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16437     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16438   };
16439   int i, j;
16440
16441   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16442   {
16443     int element = EL_CUSTOM_START + i;
16444
16445     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16446   }
16447
16448   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16449   {
16450     int element = EL_GROUP_START + i;
16451
16452     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16453   }
16454
16455   for (i = 0; i < 4; i++)
16456   {
16457     for (j = 0; j < NUM_BELT_PARTS; j++)
16458     {
16459       int element = belt_base_active_element[i] + j;
16460       int graphic = el2img(element);
16461       int anim_mode = graphic_info[graphic].anim_mode;
16462
16463       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16464       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16465     }
16466   }
16467 }
16468
16469 static void LoadEngineSnapshotValues_RND()
16470 {
16471   unsigned long num_random_calls = game.num_random_calls;
16472   int i, j;
16473
16474   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16475   {
16476     int element = EL_CUSTOM_START + i;
16477
16478     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16479   }
16480
16481   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16482   {
16483     int element = EL_GROUP_START + i;
16484
16485     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16486   }
16487
16488   for (i = 0; i < 4; i++)
16489   {
16490     for (j = 0; j < NUM_BELT_PARTS; j++)
16491     {
16492       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16493       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16494
16495       graphic_info[graphic].anim_mode = anim_mode;
16496     }
16497   }
16498
16499   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16500   {
16501     InitRND(tape.random_seed);
16502     for (i = 0; i < num_random_calls; i++)
16503       RND(1);
16504   }
16505
16506   if (game.num_random_calls != num_random_calls)
16507   {
16508     Error(ERR_INFO, "number of random calls out of sync");
16509     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16510     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16511     Error(ERR_EXIT, "this should not happen -- please debug");
16512   }
16513 }
16514
16515 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16516 {
16517   struct EngineSnapshotNodeInfo *bi =
16518     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16519
16520   bi->buffer_orig = buffer;
16521   bi->buffer_copy = checked_malloc(size);
16522   bi->size = size;
16523
16524   memcpy(bi->buffer_copy, buffer, size);
16525
16526   addNodeToList(&engine_snapshot_list, NULL, bi);
16527 }
16528
16529 void SaveEngineSnapshot()
16530 {
16531   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16532
16533   if (level_editor_test_game)   /* do not save snapshots from editor */
16534     return;
16535
16536   /* copy some special values to a structure better suited for the snapshot */
16537
16538   SaveEngineSnapshotValues_RND();
16539   SaveEngineSnapshotValues_EM();
16540
16541   /* save values stored in special snapshot structure */
16542
16543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16545
16546   /* save further RND engine values */
16547
16548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16551
16552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16556
16557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16562
16563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16566
16567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16568
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16570
16571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16573
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16592
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16595
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16599
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16602
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16608
16609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16611
16612   /* save level identification information */
16613
16614   setString(&snapshot_level_identifier, leveldir_current->identifier);
16615   snapshot_level_nr = level_nr;
16616
16617 #if 0
16618   ListNode *node = engine_snapshot_list;
16619   int num_bytes = 0;
16620
16621   while (node != NULL)
16622   {
16623     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16624
16625     node = node->next;
16626   }
16627
16628   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16629 #endif
16630 }
16631
16632 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16633 {
16634   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16635 }
16636
16637 void LoadEngineSnapshot()
16638 {
16639   ListNode *node = engine_snapshot_list;
16640
16641   if (engine_snapshot_list == NULL)
16642     return;
16643
16644   while (node != NULL)
16645   {
16646     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16647
16648     node = node->next;
16649   }
16650
16651   /* restore special values from snapshot structure */
16652
16653   LoadEngineSnapshotValues_RND();
16654   LoadEngineSnapshotValues_EM();
16655 }
16656
16657 boolean CheckEngineSnapshot()
16658 {
16659   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16660           snapshot_level_nr == level_nr);
16661 }
16662
16663
16664 /* ---------- new game button stuff ---------------------------------------- */
16665
16666 /* graphic position values for game buttons */
16667 #define GAME_BUTTON_XSIZE       30
16668 #define GAME_BUTTON_YSIZE       30
16669 #define GAME_BUTTON_XPOS        5
16670 #define GAME_BUTTON_YPOS        215
16671 #define SOUND_BUTTON_XPOS       5
16672 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16673
16674 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16675 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16676 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16677 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16678 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16679 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16680
16681 static struct
16682 {
16683   int *x, *y;
16684   int gd_x, gd_y;
16685   int gadget_id;
16686   char *infotext;
16687 } gamebutton_info[NUM_GAME_BUTTONS] =
16688 {
16689 #if 1
16690   {
16691     &game.button.stop.x,        &game.button.stop.y,
16692     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16693     GAME_CTRL_ID_STOP,
16694     "stop game"
16695   },
16696   {
16697     &game.button.pause.x,       &game.button.pause.y,
16698     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16699     GAME_CTRL_ID_PAUSE,
16700     "pause game"
16701   },
16702   {
16703     &game.button.play.x,        &game.button.play.y,
16704     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16705     GAME_CTRL_ID_PLAY,
16706     "play game"
16707   },
16708   {
16709     &game.button.sound_music.x, &game.button.sound_music.y,
16710     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16711     SOUND_CTRL_ID_MUSIC,
16712     "background music on/off"
16713   },
16714   {
16715     &game.button.sound_loops.x, &game.button.sound_loops.y,
16716     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16717     SOUND_CTRL_ID_LOOPS,
16718     "sound loops on/off"
16719   },
16720   {
16721     &game.button.sound_simple.x,&game.button.sound_simple.y,
16722     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16723     SOUND_CTRL_ID_SIMPLE,
16724     "normal sounds on/off"
16725   }
16726 #else
16727   {
16728     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16729     GAME_CTRL_ID_STOP,
16730     "stop game"
16731   },
16732   {
16733     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16734     GAME_CTRL_ID_PAUSE,
16735     "pause game"
16736   },
16737   {
16738     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16739     GAME_CTRL_ID_PLAY,
16740     "play game"
16741   },
16742   {
16743     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16744     SOUND_CTRL_ID_MUSIC,
16745     "background music on/off"
16746   },
16747   {
16748     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16749     SOUND_CTRL_ID_LOOPS,
16750     "sound loops on/off"
16751   },
16752   {
16753     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16754     SOUND_CTRL_ID_SIMPLE,
16755     "normal sounds on/off"
16756   }
16757 #endif
16758 };
16759
16760 void CreateGameButtons()
16761 {
16762   int i;
16763
16764   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16765   {
16766     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16767     struct GadgetInfo *gi;
16768     int button_type;
16769     boolean checked;
16770     unsigned long event_mask;
16771     int x, y;
16772     int gd_xoffset, gd_yoffset;
16773     int gd_x1, gd_x2, gd_y1, gd_y2;
16774     int id = i;
16775
16776     x = DX + *gamebutton_info[i].x;
16777     y = DY + *gamebutton_info[i].y;
16778     gd_xoffset = gamebutton_info[i].gd_x;
16779     gd_yoffset = gamebutton_info[i].gd_y;
16780     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16781     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16782
16783     if (id == GAME_CTRL_ID_STOP ||
16784         id == GAME_CTRL_ID_PAUSE ||
16785         id == GAME_CTRL_ID_PLAY)
16786     {
16787       button_type = GD_TYPE_NORMAL_BUTTON;
16788       checked = FALSE;
16789       event_mask = GD_EVENT_RELEASED;
16790       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16791       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16792     }
16793     else
16794     {
16795       button_type = GD_TYPE_CHECK_BUTTON;
16796       checked =
16797         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16798          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16799          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16800       event_mask = GD_EVENT_PRESSED;
16801       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16802       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16803     }
16804
16805     gi = CreateGadget(GDI_CUSTOM_ID, id,
16806                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16807 #if 1
16808                       GDI_X, x,
16809                       GDI_Y, y,
16810 #else
16811                       GDI_X, DX + gd_xoffset,
16812                       GDI_Y, DY + gd_yoffset,
16813 #endif
16814                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16815                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16816                       GDI_TYPE, button_type,
16817                       GDI_STATE, GD_BUTTON_UNPRESSED,
16818                       GDI_CHECKED, checked,
16819                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16820                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16821                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16822                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16823                       GDI_DIRECT_DRAW, FALSE,
16824                       GDI_EVENT_MASK, event_mask,
16825                       GDI_CALLBACK_ACTION, HandleGameButtons,
16826                       GDI_END);
16827
16828     if (gi == NULL)
16829       Error(ERR_EXIT, "cannot create gadget");
16830
16831     game_gadget[id] = gi;
16832   }
16833 }
16834
16835 void FreeGameButtons()
16836 {
16837   int i;
16838
16839   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16840     FreeGadget(game_gadget[i]);
16841 }
16842
16843 static void MapGameButtons()
16844 {
16845   int i;
16846
16847   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16848     MapGadget(game_gadget[i]);
16849 }
16850
16851 void UnmapGameButtons()
16852 {
16853   int i;
16854
16855   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16856     UnmapGadget(game_gadget[i]);
16857 }
16858
16859 void RedrawGameButtons()
16860 {
16861   int i;
16862
16863   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16864     RedrawGadget(game_gadget[i]);
16865 }
16866
16867 static void HandleGameButtons(struct GadgetInfo *gi)
16868 {
16869   int id = gi->custom_id;
16870
16871   if (game_status != GAME_MODE_PLAYING)
16872     return;
16873
16874   switch (id)
16875   {
16876     case GAME_CTRL_ID_STOP:
16877       if (tape.playing)
16878         TapeStop();
16879       else
16880         RequestQuitGame(TRUE);
16881       break;
16882
16883     case GAME_CTRL_ID_PAUSE:
16884       if (options.network)
16885       {
16886 #if defined(NETWORK_AVALIABLE)
16887         if (tape.pausing)
16888           SendToServer_ContinuePlaying();
16889         else
16890           SendToServer_PausePlaying();
16891 #endif
16892       }
16893       else
16894         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16895       break;
16896
16897     case GAME_CTRL_ID_PLAY:
16898       if (tape.pausing)
16899       {
16900 #if defined(NETWORK_AVALIABLE)
16901         if (options.network)
16902           SendToServer_ContinuePlaying();
16903         else
16904 #endif
16905         {
16906           tape.pausing = FALSE;
16907           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16908         }
16909       }
16910       break;
16911
16912     case SOUND_CTRL_ID_MUSIC:
16913       if (setup.sound_music)
16914       { 
16915         setup.sound_music = FALSE;
16916         FadeMusic();
16917       }
16918       else if (audio.music_available)
16919       { 
16920         setup.sound = setup.sound_music = TRUE;
16921
16922         SetAudioMode(setup.sound);
16923
16924         PlayLevelMusic();
16925       }
16926       break;
16927
16928     case SOUND_CTRL_ID_LOOPS:
16929       if (setup.sound_loops)
16930         setup.sound_loops = FALSE;
16931       else if (audio.loops_available)
16932       {
16933         setup.sound = setup.sound_loops = TRUE;
16934         SetAudioMode(setup.sound);
16935       }
16936       break;
16937
16938     case SOUND_CTRL_ID_SIMPLE:
16939       if (setup.sound_simple)
16940         setup.sound_simple = FALSE;
16941       else if (audio.sound_available)
16942       {
16943         setup.sound = setup.sound_simple = TRUE;
16944         SetAudioMode(setup.sound);
16945       }
16946       break;
16947
16948     default:
16949       break;
16950   }
16951 }