rnd-20081011-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active &&
4232           !tape.player_participates[i])
4233       {
4234         struct PlayerInfo *player = &stored_player[i];
4235         int jx = player->jx, jy = player->jy;
4236
4237         player->active = FALSE;
4238         StorePlayer[jx][jy] = 0;
4239         Feld[jx][jy] = EL_EMPTY;
4240       }
4241     }
4242 #endif
4243   }
4244   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4245   {
4246     /* when in single player mode, eliminate all but the first active player */
4247
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249     {
4250       if (stored_player[i].active)
4251       {
4252         for (j = i + 1; j < MAX_PLAYERS; j++)
4253         {
4254           if (stored_player[j].active)
4255           {
4256             struct PlayerInfo *player = &stored_player[j];
4257             int jx = player->jx, jy = player->jy;
4258
4259             player->active = FALSE;
4260             player->present = FALSE;
4261
4262             StorePlayer[jx][jy] = 0;
4263             Feld[jx][jy] = EL_EMPTY;
4264           }
4265         }
4266       }
4267     }
4268   }
4269
4270   /* when recording the game, store which players take part in the game */
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284   if (options.debug)
4285   {
4286     for (i = 0; i < MAX_PLAYERS; i++)
4287     {
4288       struct PlayerInfo *player = &stored_player[i];
4289
4290       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4291              i+1,
4292              player->present,
4293              player->connected,
4294              player->active);
4295       if (local_player == player)
4296         printf("Player  %d is local player.\n", i+1);
4297     }
4298   }
4299
4300   if (BorderElement == EL_EMPTY)
4301   {
4302     SBX_Left = 0;
4303     SBX_Right = lev_fieldx - SCR_FIELDX;
4304     SBY_Upper = 0;
4305     SBY_Lower = lev_fieldy - SCR_FIELDY;
4306   }
4307   else
4308   {
4309     SBX_Left = -1;
4310     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311     SBY_Upper = -1;
4312     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4313   }
4314
4315   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320
4321   /* if local player not found, look for custom element that might create
4322      the player (make some assumptions about the right custom element) */
4323   if (!local_player->present)
4324   {
4325     int start_x = 0, start_y = 0;
4326     int found_rating = 0;
4327     int found_element = EL_UNDEFINED;
4328     int player_nr = local_player->index_nr;
4329
4330     SCAN_PLAYFIELD(x, y)
4331     {
4332       int element = Feld[x][y];
4333       int content;
4334       int xx, yy;
4335       boolean is_player;
4336
4337       if (level.use_start_element[player_nr] &&
4338           level.start_element[player_nr] == element &&
4339           found_rating < 4)
4340       {
4341         start_x = x;
4342         start_y = y;
4343
4344         found_rating = 4;
4345         found_element = element;
4346       }
4347
4348       if (!IS_CUSTOM_ELEMENT(element))
4349         continue;
4350
4351       if (CAN_CHANGE(element))
4352       {
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as single target */
4356           content = element_info[element].change_page[i].target_element;
4357           is_player = ELEM_IS_PLAYER(content);
4358
4359           if (is_player && (found_rating < 3 ||
4360                             (found_rating == 3 && element < found_element)))
4361           {
4362             start_x = x;
4363             start_y = y;
4364
4365             found_rating = 3;
4366             found_element = element;
4367           }
4368         }
4369       }
4370
4371       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372       {
4373         /* check for player created from custom element as explosion content */
4374         content = element_info[element].content.e[xx][yy];
4375         is_player = ELEM_IS_PLAYER(content);
4376
4377         if (is_player && (found_rating < 2 ||
4378                           (found_rating == 2 && element < found_element)))
4379         {
4380           start_x = x + xx - 1;
4381           start_y = y + yy - 1;
4382
4383           found_rating = 2;
4384           found_element = element;
4385         }
4386
4387         if (!CAN_CHANGE(element))
4388           continue;
4389
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           /* check for player created from custom element as extended target */
4393           content =
4394             element_info[element].change_page[i].target_content.e[xx][yy];
4395
4396           is_player = ELEM_IS_PLAYER(content);
4397
4398           if (is_player && (found_rating < 1 ||
4399                             (found_rating == 1 && element < found_element)))
4400           {
4401             start_x = x + xx - 1;
4402             start_y = y + yy - 1;
4403
4404             found_rating = 1;
4405             found_element = element;
4406           }
4407         }
4408       }
4409     }
4410
4411     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4412                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4413                 start_x - MIDPOSX);
4414
4415     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4417                 start_y - MIDPOSY);
4418   }
4419   else
4420   {
4421     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4422                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423                 local_player->jx - MIDPOSX);
4424
4425     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427                 local_player->jy - MIDPOSY);
4428   }
4429
4430 #if 0
4431   /* do not use PLAYING mask for fading out from main screen */
4432   game_status = GAME_MODE_MAIN;
4433 #endif
4434
4435   StopAnimation();
4436
4437   if (!game.restart_level)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440 #if 1
4441   if (level_editor_test_game)
4442     FadeSkipNextFadeIn();
4443   else
4444     FadeSetEnterScreen();
4445 #else
4446   if (level_editor_test_game)
4447     fading = fading_none;
4448   else
4449     fading = menu.destination;
4450 #endif
4451
4452 #if 1
4453   FadeOut(REDRAW_FIELD);
4454 #else
4455   if (do_fading)
4456     FadeOut(REDRAW_FIELD);
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else
4472   {
4473     DrawLevel();
4474     DrawAllPlayers();
4475
4476     /* after drawing the level, correct some elements */
4477     if (game.timegate_time_left == 0)
4478       CloseAllOpenTimegates();
4479
4480     /* blit playfield from scroll buffer to normal back buffer for fading in */
4481     if (setup.soft_scrolling)
4482       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4483
4484     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4485   }
4486   /* !!! FIX THIS (END) !!! */
4487
4488 #if 1
4489   FadeIn(REDRAW_FIELD);
4490 #else
4491   if (do_fading)
4492     FadeIn(REDRAW_FIELD);
4493
4494   BackToFront();
4495 #endif
4496
4497   if (!game.restart_level)
4498   {
4499     /* copy default game door content to main double buffer */
4500     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4501                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4502   }
4503
4504   SetPanelBackground();
4505   SetDrawBackgroundMask(REDRAW_DOOR_1);
4506
4507 #if 1
4508   UpdateAndDisplayGameControlValues();
4509 #else
4510   UpdateGameDoorValues();
4511   DrawGameDoorValues();
4512 #endif
4513
4514   if (!game.restart_level)
4515   {
4516     UnmapGameButtons();
4517     UnmapTapeButtons();
4518     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4519     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4520     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4521     MapGameButtons();
4522     MapTapeButtons();
4523
4524     /* copy actual game door content to door double buffer for OpenDoor() */
4525     BlitBitmap(drawto, bitmap_db_door,
4526                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4527
4528     OpenDoor(DOOR_OPEN_ALL);
4529
4530     PlaySound(SND_GAME_STARTING);
4531
4532     if (setup.sound_music)
4533       PlayLevelMusic();
4534
4535     KeyboardAutoRepeatOffUnlessAutoplay();
4536
4537     if (options.debug)
4538     {
4539       for (i = 0; i < MAX_PLAYERS; i++)
4540         printf("Player %d %sactive.\n",
4541                i + 1, (stored_player[i].active ? "" : "not "));
4542     }
4543   }
4544
4545 #if 1
4546   UnmapAllGadgets();
4547
4548   MapGameButtons();
4549   MapTapeButtons();
4550 #endif
4551
4552   game.restart_level = FALSE;
4553 }
4554
4555 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4556 {
4557   /* this is used for non-R'n'D game engines to update certain engine values */
4558
4559   /* needed to determine if sounds are played within the visible screen area */
4560   scroll_x = actual_scroll_x;
4561   scroll_y = actual_scroll_y;
4562 }
4563
4564 void InitMovDir(int x, int y)
4565 {
4566   int i, element = Feld[x][y];
4567   static int xy[4][2] =
4568   {
4569     {  0, +1 },
4570     { +1,  0 },
4571     {  0, -1 },
4572     { -1,  0 }
4573   };
4574   static int direction[3][4] =
4575   {
4576     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4577     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4578     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4579   };
4580
4581   switch (element)
4582   {
4583     case EL_BUG_RIGHT:
4584     case EL_BUG_UP:
4585     case EL_BUG_LEFT:
4586     case EL_BUG_DOWN:
4587       Feld[x][y] = EL_BUG;
4588       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4589       break;
4590
4591     case EL_SPACESHIP_RIGHT:
4592     case EL_SPACESHIP_UP:
4593     case EL_SPACESHIP_LEFT:
4594     case EL_SPACESHIP_DOWN:
4595       Feld[x][y] = EL_SPACESHIP;
4596       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4597       break;
4598
4599     case EL_BD_BUTTERFLY_RIGHT:
4600     case EL_BD_BUTTERFLY_UP:
4601     case EL_BD_BUTTERFLY_LEFT:
4602     case EL_BD_BUTTERFLY_DOWN:
4603       Feld[x][y] = EL_BD_BUTTERFLY;
4604       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4605       break;
4606
4607     case EL_BD_FIREFLY_RIGHT:
4608     case EL_BD_FIREFLY_UP:
4609     case EL_BD_FIREFLY_LEFT:
4610     case EL_BD_FIREFLY_DOWN:
4611       Feld[x][y] = EL_BD_FIREFLY;
4612       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4613       break;
4614
4615     case EL_PACMAN_RIGHT:
4616     case EL_PACMAN_UP:
4617     case EL_PACMAN_LEFT:
4618     case EL_PACMAN_DOWN:
4619       Feld[x][y] = EL_PACMAN;
4620       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4621       break;
4622
4623     case EL_YAMYAM_LEFT:
4624     case EL_YAMYAM_RIGHT:
4625     case EL_YAMYAM_UP:
4626     case EL_YAMYAM_DOWN:
4627       Feld[x][y] = EL_YAMYAM;
4628       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4629       break;
4630
4631     case EL_SP_SNIKSNAK:
4632       MovDir[x][y] = MV_UP;
4633       break;
4634
4635     case EL_SP_ELECTRON:
4636       MovDir[x][y] = MV_LEFT;
4637       break;
4638
4639     case EL_MOLE_LEFT:
4640     case EL_MOLE_RIGHT:
4641     case EL_MOLE_UP:
4642     case EL_MOLE_DOWN:
4643       Feld[x][y] = EL_MOLE;
4644       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4645       break;
4646
4647     default:
4648       if (IS_CUSTOM_ELEMENT(element))
4649       {
4650         struct ElementInfo *ei = &element_info[element];
4651         int move_direction_initial = ei->move_direction_initial;
4652         int move_pattern = ei->move_pattern;
4653
4654         if (move_direction_initial == MV_START_PREVIOUS)
4655         {
4656           if (MovDir[x][y] != MV_NONE)
4657             return;
4658
4659           move_direction_initial = MV_START_AUTOMATIC;
4660         }
4661
4662         if (move_direction_initial == MV_START_RANDOM)
4663           MovDir[x][y] = 1 << RND(4);
4664         else if (move_direction_initial & MV_ANY_DIRECTION)
4665           MovDir[x][y] = move_direction_initial;
4666         else if (move_pattern == MV_ALL_DIRECTIONS ||
4667                  move_pattern == MV_TURNING_LEFT ||
4668                  move_pattern == MV_TURNING_RIGHT ||
4669                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4670                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4671                  move_pattern == MV_TURNING_RANDOM)
4672           MovDir[x][y] = 1 << RND(4);
4673         else if (move_pattern == MV_HORIZONTAL)
4674           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4675         else if (move_pattern == MV_VERTICAL)
4676           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4677         else if (move_pattern & MV_ANY_DIRECTION)
4678           MovDir[x][y] = element_info[element].move_pattern;
4679         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4680                  move_pattern == MV_ALONG_RIGHT_SIDE)
4681         {
4682           /* use random direction as default start direction */
4683           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4684             MovDir[x][y] = 1 << RND(4);
4685
4686           for (i = 0; i < NUM_DIRECTIONS; i++)
4687           {
4688             int x1 = x + xy[i][0];
4689             int y1 = y + xy[i][1];
4690
4691             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4692             {
4693               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4694                 MovDir[x][y] = direction[0][i];
4695               else
4696                 MovDir[x][y] = direction[1][i];
4697
4698               break;
4699             }
4700           }
4701         }                
4702       }
4703       else
4704       {
4705         MovDir[x][y] = 1 << RND(4);
4706
4707         if (element != EL_BUG &&
4708             element != EL_SPACESHIP &&
4709             element != EL_BD_BUTTERFLY &&
4710             element != EL_BD_FIREFLY)
4711           break;
4712
4713         for (i = 0; i < NUM_DIRECTIONS; i++)
4714         {
4715           int x1 = x + xy[i][0];
4716           int y1 = y + xy[i][1];
4717
4718           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4719           {
4720             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4721             {
4722               MovDir[x][y] = direction[0][i];
4723               break;
4724             }
4725             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4726                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4727             {
4728               MovDir[x][y] = direction[1][i];
4729               break;
4730             }
4731           }
4732         }
4733       }
4734       break;
4735   }
4736
4737   GfxDir[x][y] = MovDir[x][y];
4738 }
4739
4740 void InitAmoebaNr(int x, int y)
4741 {
4742   int i;
4743   int group_nr = AmoebeNachbarNr(x, y);
4744
4745   if (group_nr == 0)
4746   {
4747     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4748     {
4749       if (AmoebaCnt[i] == 0)
4750       {
4751         group_nr = i;
4752         break;
4753       }
4754     }
4755   }
4756
4757   AmoebaNr[x][y] = group_nr;
4758   AmoebaCnt[group_nr]++;
4759   AmoebaCnt2[group_nr]++;
4760 }
4761
4762 static void PlayerWins(struct PlayerInfo *player)
4763 {
4764   player->LevelSolved = TRUE;
4765   player->GameOver = TRUE;
4766
4767   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4768                          level.native_em_level->lev->score : player->score);
4769
4770   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4771   player->LevelSolved_CountingScore = player->score_final;
4772 }
4773
4774 void GameWon()
4775 {
4776   static int time, time_final;
4777   static int score, score_final;
4778   static int game_over_delay_1 = 0;
4779   static int game_over_delay_2 = 0;
4780   int game_over_delay_value_1 = 50;
4781   int game_over_delay_value_2 = 50;
4782
4783   if (!local_player->LevelSolved_GameWon)
4784   {
4785     int i;
4786
4787     /* do not start end game actions before the player stops moving (to exit) */
4788     if (local_player->MovPos)
4789       return;
4790
4791     local_player->LevelSolved_GameWon = TRUE;
4792     local_player->LevelSolved_SaveTape = tape.recording;
4793     local_player->LevelSolved_SaveScore = !tape.playing;
4794
4795     if (tape.auto_play)         /* tape might already be stopped here */
4796       tape.auto_play_level_solved = TRUE;
4797
4798 #if 1
4799     TapeStop();
4800 #endif
4801
4802     game_over_delay_1 = game_over_delay_value_1;
4803     game_over_delay_2 = game_over_delay_value_2;
4804
4805     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4806     score = score_final = local_player->score_final;
4807
4808     if (TimeLeft > 0)
4809     {
4810       time_final = 0;
4811       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4812     }
4813     else if (level.time == 0 && TimePlayed < 999)
4814     {
4815       time_final = 999;
4816       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4817     }
4818
4819     local_player->score_final = score_final;
4820
4821     if (level_editor_test_game)
4822     {
4823       time = time_final;
4824       score = score_final;
4825
4826 #if 1
4827       local_player->LevelSolved_CountingTime = time;
4828       local_player->LevelSolved_CountingScore = score;
4829
4830       game_panel_controls[GAME_PANEL_TIME].value = time;
4831       game_panel_controls[GAME_PANEL_SCORE].value = score;
4832
4833       DisplayGameControlValues();
4834 #else
4835       DrawGameValue_Time(time);
4836       DrawGameValue_Score(score);
4837 #endif
4838     }
4839
4840     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4841     {
4842       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4843       {
4844         /* close exit door after last player */
4845         if ((AllPlayersGone &&
4846              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4847               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4848               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4849             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4850             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4851         {
4852           int element = Feld[ExitX][ExitY];
4853
4854 #if 0
4855           if (element == EL_EM_EXIT_OPEN ||
4856               element == EL_EM_STEEL_EXIT_OPEN)
4857           {
4858             Bang(ExitX, ExitY);
4859           }
4860           else
4861 #endif
4862           {
4863             Feld[ExitX][ExitY] =
4864               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4865                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4866                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4867                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4868                EL_EM_STEEL_EXIT_CLOSING);
4869
4870             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4871           }
4872         }
4873
4874         /* player disappears */
4875         DrawLevelField(ExitX, ExitY);
4876       }
4877
4878       for (i = 0; i < MAX_PLAYERS; i++)
4879       {
4880         struct PlayerInfo *player = &stored_player[i];
4881
4882         if (player->present)
4883         {
4884           RemovePlayer(player);
4885
4886           /* player disappears */
4887           DrawLevelField(player->jx, player->jy);
4888         }
4889       }
4890     }
4891
4892     PlaySound(SND_GAME_WINNING);
4893   }
4894
4895   if (game_over_delay_1 > 0)
4896   {
4897     game_over_delay_1--;
4898
4899     return;
4900   }
4901
4902   if (time != time_final)
4903   {
4904     int time_to_go = ABS(time_final - time);
4905     int time_count_dir = (time < time_final ? +1 : -1);
4906     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4907
4908     time  += time_count_steps * time_count_dir;
4909     score += time_count_steps * level.score[SC_TIME_BONUS];
4910
4911 #if 1
4912     local_player->LevelSolved_CountingTime = time;
4913     local_player->LevelSolved_CountingScore = score;
4914
4915     game_panel_controls[GAME_PANEL_TIME].value = time;
4916     game_panel_controls[GAME_PANEL_SCORE].value = score;
4917
4918     DisplayGameControlValues();
4919 #else
4920     DrawGameValue_Time(time);
4921     DrawGameValue_Score(score);
4922 #endif
4923
4924     if (time == time_final)
4925       StopSound(SND_GAME_LEVELTIME_BONUS);
4926     else if (setup.sound_loops)
4927       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4928     else
4929       PlaySound(SND_GAME_LEVELTIME_BONUS);
4930
4931     return;
4932   }
4933
4934   local_player->LevelSolved_PanelOff = TRUE;
4935
4936   if (game_over_delay_2 > 0)
4937   {
4938     game_over_delay_2--;
4939
4940     return;
4941   }
4942
4943 #if 1
4944   GameEnd();
4945 #endif
4946 }
4947
4948 void GameEnd()
4949 {
4950   int hi_pos;
4951   boolean raise_level = FALSE;
4952
4953   local_player->LevelSolved_GameEnd = TRUE;
4954
4955   CloseDoor(DOOR_CLOSE_1);
4956
4957   if (local_player->LevelSolved_SaveTape)
4958   {
4959 #if 0
4960     TapeStop();
4961 #endif
4962
4963 #if 1
4964     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4965 #else
4966     SaveTape(tape.level_nr);            /* ask to save tape */
4967 #endif
4968   }
4969
4970   if (level_editor_test_game)
4971   {
4972     game_status = GAME_MODE_MAIN;
4973
4974 #if 1
4975     DrawAndFadeInMainMenu(REDRAW_FIELD);
4976 #else
4977     DrawMainMenu();
4978 #endif
4979
4980     return;
4981   }
4982
4983   if (!local_player->LevelSolved_SaveScore)
4984   {
4985 #if 1
4986     FadeOut(REDRAW_FIELD);
4987 #endif
4988
4989     game_status = GAME_MODE_MAIN;
4990
4991     DrawAndFadeInMainMenu(REDRAW_FIELD);
4992
4993     return;
4994   }
4995
4996   if (level_nr == leveldir_current->handicap_level)
4997   {
4998     leveldir_current->handicap_level++;
4999     SaveLevelSetup_SeriesInfo();
5000   }
5001
5002   if (level_nr < leveldir_current->last_level)
5003     raise_level = TRUE;                 /* advance to next level */
5004
5005   if ((hi_pos = NewHiScore()) >= 0) 
5006   {
5007     game_status = GAME_MODE_SCORES;
5008
5009     DrawHallOfFame(hi_pos);
5010
5011     if (raise_level)
5012     {
5013       level_nr++;
5014       TapeErase();
5015     }
5016   }
5017   else
5018   {
5019 #if 1
5020     FadeOut(REDRAW_FIELD);
5021 #endif
5022
5023     game_status = GAME_MODE_MAIN;
5024
5025     if (raise_level)
5026     {
5027       level_nr++;
5028       TapeErase();
5029     }
5030
5031     DrawAndFadeInMainMenu(REDRAW_FIELD);
5032   }
5033 }
5034
5035 int NewHiScore()
5036 {
5037   int k, l;
5038   int position = -1;
5039
5040   LoadScore(level_nr);
5041
5042   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5043       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5044     return -1;
5045
5046   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5047   {
5048     if (local_player->score_final > highscore[k].Score)
5049     {
5050       /* player has made it to the hall of fame */
5051
5052       if (k < MAX_SCORE_ENTRIES - 1)
5053       {
5054         int m = MAX_SCORE_ENTRIES - 1;
5055
5056 #ifdef ONE_PER_NAME
5057         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5058           if (strEqual(setup.player_name, highscore[l].Name))
5059             m = l;
5060         if (m == k)     /* player's new highscore overwrites his old one */
5061           goto put_into_list;
5062 #endif
5063
5064         for (l = m; l > k; l--)
5065         {
5066           strcpy(highscore[l].Name, highscore[l - 1].Name);
5067           highscore[l].Score = highscore[l - 1].Score;
5068         }
5069       }
5070
5071 #ifdef ONE_PER_NAME
5072       put_into_list:
5073 #endif
5074       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5075       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5076       highscore[k].Score = local_player->score_final; 
5077       position = k;
5078       break;
5079     }
5080
5081 #ifdef ONE_PER_NAME
5082     else if (!strncmp(setup.player_name, highscore[k].Name,
5083                       MAX_PLAYER_NAME_LEN))
5084       break;    /* player already there with a higher score */
5085 #endif
5086
5087   }
5088
5089   if (position >= 0) 
5090     SaveScore(level_nr);
5091
5092   return position;
5093 }
5094
5095 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5096 {
5097   int element = Feld[x][y];
5098   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5099   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5100   int horiz_move = (dx != 0);
5101   int sign = (horiz_move ? dx : dy);
5102   int step = sign * element_info[element].move_stepsize;
5103
5104   /* special values for move stepsize for spring and things on conveyor belt */
5105   if (horiz_move)
5106   {
5107     if (CAN_FALL(element) &&
5108         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5109       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5110     else if (element == EL_SPRING)
5111       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5112   }
5113
5114   return step;
5115 }
5116
5117 inline static int getElementMoveStepsize(int x, int y)
5118 {
5119   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5120 }
5121
5122 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5123 {
5124   if (player->GfxAction != action || player->GfxDir != dir)
5125   {
5126 #if 0
5127     printf("Player frame reset! (%d => %d, %d => %d)\n",
5128            player->GfxAction, action, player->GfxDir, dir);
5129 #endif
5130
5131     player->GfxAction = action;
5132     player->GfxDir = dir;
5133     player->Frame = 0;
5134     player->StepFrame = 0;
5135   }
5136 }
5137
5138 #if USE_GFX_RESET_GFX_ANIMATION
5139 static void ResetGfxFrame(int x, int y, boolean redraw)
5140 {
5141   int element = Feld[x][y];
5142   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5143   int last_gfx_frame = GfxFrame[x][y];
5144
5145   if (graphic_info[graphic].anim_global_sync)
5146     GfxFrame[x][y] = FrameCounter;
5147   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5148     GfxFrame[x][y] = CustomValue[x][y];
5149   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5150     GfxFrame[x][y] = element_info[element].collect_score;
5151   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5152     GfxFrame[x][y] = ChangeDelay[x][y];
5153
5154   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5155     DrawLevelGraphicAnimation(x, y, graphic);
5156 }
5157 #endif
5158
5159 static void ResetGfxAnimation(int x, int y)
5160 {
5161   GfxAction[x][y] = ACTION_DEFAULT;
5162   GfxDir[x][y] = MovDir[x][y];
5163   GfxFrame[x][y] = 0;
5164
5165 #if USE_GFX_RESET_GFX_ANIMATION
5166   ResetGfxFrame(x, y, FALSE);
5167 #endif
5168 }
5169
5170 static void ResetRandomAnimationValue(int x, int y)
5171 {
5172   GfxRandom[x][y] = INIT_GFX_RANDOM();
5173 }
5174
5175 void InitMovingField(int x, int y, int direction)
5176 {
5177   int element = Feld[x][y];
5178   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5179   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5180   int newx = x + dx;
5181   int newy = y + dy;
5182   boolean is_moving_before, is_moving_after;
5183 #if 0
5184   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5185 #endif
5186
5187   /* check if element was/is moving or being moved before/after mode change */
5188 #if 1
5189 #if 1
5190   is_moving_before = (WasJustMoving[x][y] != 0);
5191 #else
5192   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5193   is_moving_before = WasJustMoving[x][y];
5194 #endif
5195 #else
5196   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5197 #endif
5198   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5199
5200   /* reset animation only for moving elements which change direction of moving
5201      or which just started or stopped moving
5202      (else CEs with property "can move" / "not moving" are reset each frame) */
5203 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5204 #if 1
5205   if (is_moving_before != is_moving_after ||
5206       direction != MovDir[x][y])
5207     ResetGfxAnimation(x, y);
5208 #else
5209   if ((is_moving_before || is_moving_after) && !continues_moving)
5210     ResetGfxAnimation(x, y);
5211 #endif
5212 #else
5213   if (!continues_moving)
5214     ResetGfxAnimation(x, y);
5215 #endif
5216
5217   MovDir[x][y] = direction;
5218   GfxDir[x][y] = direction;
5219
5220 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5221   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5222                      direction == MV_DOWN && CAN_FALL(element) ?
5223                      ACTION_FALLING : ACTION_MOVING);
5224 #else
5225   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5226                      ACTION_FALLING : ACTION_MOVING);
5227 #endif
5228
5229   /* this is needed for CEs with property "can move" / "not moving" */
5230
5231   if (is_moving_after)
5232   {
5233     if (Feld[newx][newy] == EL_EMPTY)
5234       Feld[newx][newy] = EL_BLOCKED;
5235
5236     MovDir[newx][newy] = MovDir[x][y];
5237
5238 #if USE_NEW_CUSTOM_VALUE
5239     CustomValue[newx][newy] = CustomValue[x][y];
5240 #endif
5241
5242     GfxFrame[newx][newy] = GfxFrame[x][y];
5243     GfxRandom[newx][newy] = GfxRandom[x][y];
5244     GfxAction[newx][newy] = GfxAction[x][y];
5245     GfxDir[newx][newy] = GfxDir[x][y];
5246   }
5247 }
5248
5249 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5250 {
5251   int direction = MovDir[x][y];
5252   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5253   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5254
5255   *goes_to_x = newx;
5256   *goes_to_y = newy;
5257 }
5258
5259 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5260 {
5261   int oldx = x, oldy = y;
5262   int direction = MovDir[x][y];
5263
5264   if (direction == MV_LEFT)
5265     oldx++;
5266   else if (direction == MV_RIGHT)
5267     oldx--;
5268   else if (direction == MV_UP)
5269     oldy++;
5270   else if (direction == MV_DOWN)
5271     oldy--;
5272
5273   *comes_from_x = oldx;
5274   *comes_from_y = oldy;
5275 }
5276
5277 int MovingOrBlocked2Element(int x, int y)
5278 {
5279   int element = Feld[x][y];
5280
5281   if (element == EL_BLOCKED)
5282   {
5283     int oldx, oldy;
5284
5285     Blocked2Moving(x, y, &oldx, &oldy);
5286     return Feld[oldx][oldy];
5287   }
5288   else
5289     return element;
5290 }
5291
5292 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5293 {
5294   /* like MovingOrBlocked2Element(), but if element is moving
5295      and (x,y) is the field the moving element is just leaving,
5296      return EL_BLOCKED instead of the element value */
5297   int element = Feld[x][y];
5298
5299   if (IS_MOVING(x, y))
5300   {
5301     if (element == EL_BLOCKED)
5302     {
5303       int oldx, oldy;
5304
5305       Blocked2Moving(x, y, &oldx, &oldy);
5306       return Feld[oldx][oldy];
5307     }
5308     else
5309       return EL_BLOCKED;
5310   }
5311   else
5312     return element;
5313 }
5314
5315 static void RemoveField(int x, int y)
5316 {
5317   Feld[x][y] = EL_EMPTY;
5318
5319   MovPos[x][y] = 0;
5320   MovDir[x][y] = 0;
5321   MovDelay[x][y] = 0;
5322
5323 #if USE_NEW_CUSTOM_VALUE
5324   CustomValue[x][y] = 0;
5325 #endif
5326
5327   AmoebaNr[x][y] = 0;
5328   ChangeDelay[x][y] = 0;
5329   ChangePage[x][y] = -1;
5330   Pushed[x][y] = FALSE;
5331
5332 #if 0
5333   ExplodeField[x][y] = EX_TYPE_NONE;
5334 #endif
5335
5336   GfxElement[x][y] = EL_UNDEFINED;
5337   GfxAction[x][y] = ACTION_DEFAULT;
5338   GfxDir[x][y] = MV_NONE;
5339 #if 0
5340   /* !!! this would prevent the removed tile from being redrawn !!! */
5341   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5342 #endif
5343 }
5344
5345 void RemoveMovingField(int x, int y)
5346 {
5347   int oldx = x, oldy = y, newx = x, newy = y;
5348   int element = Feld[x][y];
5349   int next_element = EL_UNDEFINED;
5350
5351   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5352     return;
5353
5354   if (IS_MOVING(x, y))
5355   {
5356     Moving2Blocked(x, y, &newx, &newy);
5357
5358     if (Feld[newx][newy] != EL_BLOCKED)
5359     {
5360       /* element is moving, but target field is not free (blocked), but
5361          already occupied by something different (example: acid pool);
5362          in this case, only remove the moving field, but not the target */
5363
5364       RemoveField(oldx, oldy);
5365
5366       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5367
5368       TEST_DrawLevelField(oldx, oldy);
5369
5370       return;
5371     }
5372   }
5373   else if (element == EL_BLOCKED)
5374   {
5375     Blocked2Moving(x, y, &oldx, &oldy);
5376     if (!IS_MOVING(oldx, oldy))
5377       return;
5378   }
5379
5380   if (element == EL_BLOCKED &&
5381       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5382        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5383        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5384        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5385        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5386        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5387     next_element = get_next_element(Feld[oldx][oldy]);
5388
5389   RemoveField(oldx, oldy);
5390   RemoveField(newx, newy);
5391
5392   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5393
5394   if (next_element != EL_UNDEFINED)
5395     Feld[oldx][oldy] = next_element;
5396
5397   TEST_DrawLevelField(oldx, oldy);
5398   TEST_DrawLevelField(newx, newy);
5399 }
5400
5401 void DrawDynamite(int x, int y)
5402 {
5403   int sx = SCREENX(x), sy = SCREENY(y);
5404   int graphic = el2img(Feld[x][y]);
5405   int frame;
5406
5407   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5408     return;
5409
5410   if (IS_WALKABLE_INSIDE(Back[x][y]))
5411     return;
5412
5413   if (Back[x][y])
5414     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5415   else if (Store[x][y])
5416     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5417
5418   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5419
5420   if (Back[x][y] || Store[x][y])
5421     DrawGraphicThruMask(sx, sy, graphic, frame);
5422   else
5423     DrawGraphic(sx, sy, graphic, frame);
5424 }
5425
5426 void CheckDynamite(int x, int y)
5427 {
5428   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5429   {
5430     MovDelay[x][y]--;
5431
5432     if (MovDelay[x][y] != 0)
5433     {
5434       DrawDynamite(x, y);
5435       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5436
5437       return;
5438     }
5439   }
5440
5441   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5442
5443   Bang(x, y);
5444 }
5445
5446 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5447 {
5448   boolean num_checked_players = 0;
5449   int i;
5450
5451   for (i = 0; i < MAX_PLAYERS; i++)
5452   {
5453     if (stored_player[i].active)
5454     {
5455       int sx = stored_player[i].jx;
5456       int sy = stored_player[i].jy;
5457
5458       if (num_checked_players == 0)
5459       {
5460         *sx1 = *sx2 = sx;
5461         *sy1 = *sy2 = sy;
5462       }
5463       else
5464       {
5465         *sx1 = MIN(*sx1, sx);
5466         *sy1 = MIN(*sy1, sy);
5467         *sx2 = MAX(*sx2, sx);
5468         *sy2 = MAX(*sy2, sy);
5469       }
5470
5471       num_checked_players++;
5472     }
5473   }
5474 }
5475
5476 static boolean checkIfAllPlayersFitToScreen_RND()
5477 {
5478   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5479
5480   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5481
5482   return (sx2 - sx1 < SCR_FIELDX &&
5483           sy2 - sy1 < SCR_FIELDY);
5484 }
5485
5486 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5487 {
5488   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5489
5490   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5491
5492   *sx = (sx1 + sx2) / 2;
5493   *sy = (sy1 + sy2) / 2;
5494 }
5495
5496 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5497                         boolean center_screen, boolean quick_relocation)
5498 {
5499   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5500   boolean no_delay = (tape.warp_forward);
5501   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5502   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5503
5504   if (quick_relocation)
5505   {
5506     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5507     {
5508       if (!level.shifted_relocation || center_screen)
5509       {
5510         /* quick relocation (without scrolling), with centering of screen */
5511
5512         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5513                     x > SBX_Right + MIDPOSX ? SBX_Right :
5514                     x - MIDPOSX);
5515
5516         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5517                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5518                     y - MIDPOSY);
5519       }
5520       else
5521       {
5522         /* quick relocation (without scrolling), but do not center screen */
5523
5524         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5525                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5526                                old_x - MIDPOSX);
5527
5528         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5529                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5530                                old_y - MIDPOSY);
5531
5532         int offset_x = x + (scroll_x - center_scroll_x);
5533         int offset_y = y + (scroll_y - center_scroll_y);
5534
5535         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5536                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5537                     offset_x - MIDPOSX);
5538
5539         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5540                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5541                     offset_y - MIDPOSY);
5542       }
5543     }
5544     else
5545     {
5546 #if 1
5547       if (!level.shifted_relocation || center_screen)
5548       {
5549         /* quick relocation (without scrolling), with centering of screen */
5550
5551         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5552                     x > SBX_Right + MIDPOSX ? SBX_Right :
5553                     x - MIDPOSX);
5554
5555         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5556                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5557                     y - MIDPOSY);
5558       }
5559       else
5560       {
5561         /* quick relocation (without scrolling), but do not center screen */
5562
5563         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5564                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5565                                old_x - MIDPOSX);
5566
5567         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5568                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5569                                old_y - MIDPOSY);
5570
5571         int offset_x = x + (scroll_x - center_scroll_x);
5572         int offset_y = y + (scroll_y - center_scroll_y);
5573
5574         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5575                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5576                     offset_x - MIDPOSX);
5577
5578         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5579                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5580                     offset_y - MIDPOSY);
5581       }
5582 #else
5583       /* quick relocation (without scrolling), inside visible screen area */
5584
5585       int offset = game.scroll_delay_value;
5586
5587       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5588           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5589         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5590
5591       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5592           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5593         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5594
5595       /* don't scroll over playfield boundaries */
5596       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5597         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5598
5599       /* don't scroll over playfield boundaries */
5600       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5601         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5602 #endif
5603     }
5604
5605     RedrawPlayfield(TRUE, 0,0,0,0);
5606   }
5607   else
5608   {
5609 #if 1
5610     int scroll_xx, scroll_yy;
5611
5612     if (!level.shifted_relocation || center_screen)
5613     {
5614       /* visible relocation (with scrolling), with centering of screen */
5615
5616       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5617                    x > SBX_Right + MIDPOSX ? SBX_Right :
5618                    x - MIDPOSX);
5619
5620       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622                    y - MIDPOSY);
5623     }
5624     else
5625     {
5626       /* visible relocation (with scrolling), but do not center screen */
5627
5628       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5629                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5630                              old_x - MIDPOSX);
5631
5632       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5633                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5634                              old_y - MIDPOSY);
5635
5636       int offset_x = x + (scroll_x - center_scroll_x);
5637       int offset_y = y + (scroll_y - center_scroll_y);
5638
5639       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5640                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5641                    offset_x - MIDPOSX);
5642
5643       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645                    offset_y - MIDPOSY);
5646     }
5647
5648 #else
5649
5650     /* visible relocation (with scrolling), with centering of screen */
5651
5652     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5653                      x > SBX_Right + MIDPOSX ? SBX_Right :
5654                      x - MIDPOSX);
5655
5656     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5657                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5658                      y - MIDPOSY);
5659 #endif
5660
5661     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5662
5663     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5664     {
5665       int dx = 0, dy = 0;
5666       int fx = FX, fy = FY;
5667
5668       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5669       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5670
5671       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5672         break;
5673
5674       scroll_x -= dx;
5675       scroll_y -= dy;
5676
5677       fx += dx * TILEX / 2;
5678       fy += dy * TILEY / 2;
5679
5680       ScrollLevel(dx, dy);
5681       DrawAllPlayers();
5682
5683       /* scroll in two steps of half tile size to make things smoother */
5684       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5685       FlushDisplay();
5686       Delay(wait_delay_value);
5687
5688       /* scroll second step to align at full tile size */
5689       BackToFront();
5690       Delay(wait_delay_value);
5691     }
5692
5693     DrawAllPlayers();
5694     BackToFront();
5695     Delay(wait_delay_value);
5696   }
5697 }
5698
5699 void RelocatePlayer(int jx, int jy, int el_player_raw)
5700 {
5701   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5702   int player_nr = GET_PLAYER_NR(el_player);
5703   struct PlayerInfo *player = &stored_player[player_nr];
5704   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5705   boolean no_delay = (tape.warp_forward);
5706   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5707   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5708   int old_jx = player->jx;
5709   int old_jy = player->jy;
5710   int old_element = Feld[old_jx][old_jy];
5711   int element = Feld[jx][jy];
5712   boolean player_relocated = (old_jx != jx || old_jy != jy);
5713
5714   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5715   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5716   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5717   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5718   int leave_side_horiz = move_dir_horiz;
5719   int leave_side_vert  = move_dir_vert;
5720   int enter_side = enter_side_horiz | enter_side_vert;
5721   int leave_side = leave_side_horiz | leave_side_vert;
5722
5723   if (player->GameOver)         /* do not reanimate dead player */
5724     return;
5725
5726   if (!player_relocated)        /* no need to relocate the player */
5727     return;
5728
5729   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5730   {
5731     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5732     DrawLevelField(jx, jy);
5733   }
5734
5735   if (player->present)
5736   {
5737     while (player->MovPos)
5738     {
5739       ScrollPlayer(player, SCROLL_GO_ON);
5740       ScrollScreen(NULL, SCROLL_GO_ON);
5741
5742       AdvanceFrameAndPlayerCounters(player->index_nr);
5743
5744       DrawPlayer(player);
5745
5746       BackToFront();
5747       Delay(wait_delay_value);
5748     }
5749
5750     DrawPlayer(player);         /* needed here only to cleanup last field */
5751     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5752
5753     player->is_moving = FALSE;
5754   }
5755
5756   if (IS_CUSTOM_ELEMENT(old_element))
5757     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5758                                CE_LEFT_BY_PLAYER,
5759                                player->index_bit, leave_side);
5760
5761   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5762                                       CE_PLAYER_LEAVES_X,
5763                                       player->index_bit, leave_side);
5764
5765   Feld[jx][jy] = el_player;
5766   InitPlayerField(jx, jy, el_player, TRUE);
5767
5768   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5769   {
5770     Feld[jx][jy] = element;
5771     InitField(jx, jy, FALSE);
5772   }
5773
5774   /* only visually relocate centered player */
5775   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5776                      FALSE, level.instant_relocation);
5777
5778   TestIfPlayerTouchesBadThing(jx, jy);
5779   TestIfPlayerTouchesCustomElement(jx, jy);
5780
5781   if (IS_CUSTOM_ELEMENT(element))
5782     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5783                                player->index_bit, enter_side);
5784
5785   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5786                                       player->index_bit, enter_side);
5787
5788 #if 1
5789   if (player->is_switching)
5790   {
5791     /* ensure that relocation while still switching an element does not cause
5792        a new element to be treated as also switched directly after relocation
5793        (this is important for teleporter switches that teleport the player to
5794        a place where another teleporter switch is in the same direction, which
5795        would then incorrectly be treated as immediately switched before the
5796        direction key that caused the switch was released) */
5797
5798     player->switch_x += jx - old_jx;
5799     player->switch_y += jy - old_jy;
5800   }
5801 #endif
5802 }
5803
5804 void Explode(int ex, int ey, int phase, int mode)
5805 {
5806   int x, y;
5807   int last_phase;
5808   int border_element;
5809
5810   /* !!! eliminate this variable !!! */
5811   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5812
5813   if (game.explosions_delayed)
5814   {
5815     ExplodeField[ex][ey] = mode;
5816     return;
5817   }
5818
5819   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5820   {
5821     int center_element = Feld[ex][ey];
5822     int artwork_element, explosion_element;     /* set these values later */
5823
5824 #if 0
5825     /* --- This is only really needed (and now handled) in "Impact()". --- */
5826     /* do not explode moving elements that left the explode field in time */
5827     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5828         center_element == EL_EMPTY &&
5829         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5830       return;
5831 #endif
5832
5833 #if 0
5834     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5835     if (mode == EX_TYPE_NORMAL ||
5836         mode == EX_TYPE_CENTER ||
5837         mode == EX_TYPE_CROSS)
5838       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5839 #endif
5840
5841     /* remove things displayed in background while burning dynamite */
5842     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5843       Back[ex][ey] = 0;
5844
5845     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5846     {
5847       /* put moving element to center field (and let it explode there) */
5848       center_element = MovingOrBlocked2Element(ex, ey);
5849       RemoveMovingField(ex, ey);
5850       Feld[ex][ey] = center_element;
5851     }
5852
5853     /* now "center_element" is finally determined -- set related values now */
5854     artwork_element = center_element;           /* for custom player artwork */
5855     explosion_element = center_element;         /* for custom player artwork */
5856
5857     if (IS_PLAYER(ex, ey))
5858     {
5859       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5860
5861       artwork_element = stored_player[player_nr].artwork_element;
5862
5863       if (level.use_explosion_element[player_nr])
5864       {
5865         explosion_element = level.explosion_element[player_nr];
5866         artwork_element = explosion_element;
5867       }
5868     }
5869
5870 #if 1
5871     if (mode == EX_TYPE_NORMAL ||
5872         mode == EX_TYPE_CENTER ||
5873         mode == EX_TYPE_CROSS)
5874       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5875 #endif
5876
5877     last_phase = element_info[explosion_element].explosion_delay + 1;
5878
5879     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5880     {
5881       int xx = x - ex + 1;
5882       int yy = y - ey + 1;
5883       int element;
5884
5885       if (!IN_LEV_FIELD(x, y) ||
5886           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5887           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5888         continue;
5889
5890       element = Feld[x][y];
5891
5892       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5893       {
5894         element = MovingOrBlocked2Element(x, y);
5895
5896         if (!IS_EXPLOSION_PROOF(element))
5897           RemoveMovingField(x, y);
5898       }
5899
5900       /* indestructible elements can only explode in center (but not flames) */
5901       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5902                                            mode == EX_TYPE_BORDER)) ||
5903           element == EL_FLAMES)
5904         continue;
5905
5906       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5907          behaviour, for example when touching a yamyam that explodes to rocks
5908          with active deadly shield, a rock is created under the player !!! */
5909       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5910 #if 0
5911       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5912           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5913            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5914 #else
5915       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5916 #endif
5917       {
5918         if (IS_ACTIVE_BOMB(element))
5919         {
5920           /* re-activate things under the bomb like gate or penguin */
5921           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5922           Back[x][y] = 0;
5923         }
5924
5925         continue;
5926       }
5927
5928       /* save walkable background elements while explosion on same tile */
5929       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5930           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5931         Back[x][y] = element;
5932
5933       /* ignite explodable elements reached by other explosion */
5934       if (element == EL_EXPLOSION)
5935         element = Store2[x][y];
5936
5937       if (AmoebaNr[x][y] &&
5938           (element == EL_AMOEBA_FULL ||
5939            element == EL_BD_AMOEBA ||
5940            element == EL_AMOEBA_GROWING))
5941       {
5942         AmoebaCnt[AmoebaNr[x][y]]--;
5943         AmoebaCnt2[AmoebaNr[x][y]]--;
5944       }
5945
5946       RemoveField(x, y);
5947
5948       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5949       {
5950         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5951
5952         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5953
5954         if (PLAYERINFO(ex, ey)->use_murphy)
5955           Store[x][y] = EL_EMPTY;
5956       }
5957
5958       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5959          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5960       else if (ELEM_IS_PLAYER(center_element))
5961         Store[x][y] = EL_EMPTY;
5962       else if (center_element == EL_YAMYAM)
5963         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5964       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5965         Store[x][y] = element_info[center_element].content.e[xx][yy];
5966 #if 1
5967       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5968          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5969          otherwise) -- FIX THIS !!! */
5970       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5971         Store[x][y] = element_info[element].content.e[1][1];
5972 #else
5973       else if (!CAN_EXPLODE(element))
5974         Store[x][y] = element_info[element].content.e[1][1];
5975 #endif
5976       else
5977         Store[x][y] = EL_EMPTY;
5978
5979       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5980           center_element == EL_AMOEBA_TO_DIAMOND)
5981         Store2[x][y] = element;
5982
5983       Feld[x][y] = EL_EXPLOSION;
5984       GfxElement[x][y] = artwork_element;
5985
5986       ExplodePhase[x][y] = 1;
5987       ExplodeDelay[x][y] = last_phase;
5988
5989       Stop[x][y] = TRUE;
5990     }
5991
5992     if (center_element == EL_YAMYAM)
5993       game.yamyam_content_nr =
5994         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5995
5996     return;
5997   }
5998
5999   if (Stop[ex][ey])
6000     return;
6001
6002   x = ex;
6003   y = ey;
6004
6005   if (phase == 1)
6006     GfxFrame[x][y] = 0;         /* restart explosion animation */
6007
6008   last_phase = ExplodeDelay[x][y];
6009
6010   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6011
6012 #ifdef DEBUG
6013
6014   /* activate this even in non-DEBUG version until cause for crash in
6015      getGraphicAnimationFrame() (see below) is found and eliminated */
6016
6017 #endif
6018 #if 1
6019
6020 #if 1
6021   /* this can happen if the player leaves an explosion just in time */
6022   if (GfxElement[x][y] == EL_UNDEFINED)
6023     GfxElement[x][y] = EL_EMPTY;
6024 #else
6025   if (GfxElement[x][y] == EL_UNDEFINED)
6026   {
6027     printf("\n\n");
6028     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6029     printf("Explode(): This should never happen!\n");
6030     printf("\n\n");
6031
6032     GfxElement[x][y] = EL_EMPTY;
6033   }
6034 #endif
6035
6036 #endif
6037
6038   border_element = Store2[x][y];
6039   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6040     border_element = StorePlayer[x][y];
6041
6042   if (phase == element_info[border_element].ignition_delay ||
6043       phase == last_phase)
6044   {
6045     boolean border_explosion = FALSE;
6046
6047     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6048         !PLAYER_EXPLOSION_PROTECTED(x, y))
6049     {
6050       KillPlayerUnlessExplosionProtected(x, y);
6051       border_explosion = TRUE;
6052     }
6053     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6054     {
6055       Feld[x][y] = Store2[x][y];
6056       Store2[x][y] = 0;
6057       Bang(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6061     {
6062       AmoebeUmwandeln(x, y);
6063       Store2[x][y] = 0;
6064       border_explosion = TRUE;
6065     }
6066
6067     /* if an element just explodes due to another explosion (chain-reaction),
6068        do not immediately end the new explosion when it was the last frame of
6069        the explosion (as it would be done in the following "if"-statement!) */
6070     if (border_explosion && phase == last_phase)
6071       return;
6072   }
6073
6074   if (phase == last_phase)
6075   {
6076     int element;
6077
6078     element = Feld[x][y] = Store[x][y];
6079     Store[x][y] = Store2[x][y] = 0;
6080     GfxElement[x][y] = EL_UNDEFINED;
6081
6082     /* player can escape from explosions and might therefore be still alive */
6083     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6084         element <= EL_PLAYER_IS_EXPLODING_4)
6085     {
6086       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6087       int explosion_element = EL_PLAYER_1 + player_nr;
6088       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6089       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6090
6091       if (level.use_explosion_element[player_nr])
6092         explosion_element = level.explosion_element[player_nr];
6093
6094       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6095                     element_info[explosion_element].content.e[xx][yy]);
6096     }
6097
6098     /* restore probably existing indestructible background element */
6099     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6100       element = Feld[x][y] = Back[x][y];
6101     Back[x][y] = 0;
6102
6103     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6104     GfxDir[x][y] = MV_NONE;
6105     ChangeDelay[x][y] = 0;
6106     ChangePage[x][y] = -1;
6107
6108 #if USE_NEW_CUSTOM_VALUE
6109     CustomValue[x][y] = 0;
6110 #endif
6111
6112     InitField_WithBug2(x, y, FALSE);
6113
6114     TEST_DrawLevelField(x, y);
6115
6116     TestIfElementTouchesCustomElement(x, y);
6117
6118     if (GFX_CRUMBLED(element))
6119       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6120
6121     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6122       StorePlayer[x][y] = 0;
6123
6124     if (ELEM_IS_PLAYER(element))
6125       RelocatePlayer(x, y, element);
6126   }
6127   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6128   {
6129     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6130     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6131
6132     if (phase == delay)
6133       TEST_DrawLevelFieldCrumbledSand(x, y);
6134
6135     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6136     {
6137       DrawLevelElement(x, y, Back[x][y]);
6138       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6139     }
6140     else if (IS_WALKABLE_UNDER(Back[x][y]))
6141     {
6142       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6143       DrawLevelElementThruMask(x, y, Back[x][y]);
6144     }
6145     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6146       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6147   }
6148 }
6149
6150 void DynaExplode(int ex, int ey)
6151 {
6152   int i, j;
6153   int dynabomb_element = Feld[ex][ey];
6154   int dynabomb_size = 1;
6155   boolean dynabomb_xl = FALSE;
6156   struct PlayerInfo *player;
6157   static int xy[4][2] =
6158   {
6159     { 0, -1 },
6160     { -1, 0 },
6161     { +1, 0 },
6162     { 0, +1 }
6163   };
6164
6165   if (IS_ACTIVE_BOMB(dynabomb_element))
6166   {
6167     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6168     dynabomb_size = player->dynabomb_size;
6169     dynabomb_xl = player->dynabomb_xl;
6170     player->dynabombs_left++;
6171   }
6172
6173   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6174
6175   for (i = 0; i < NUM_DIRECTIONS; i++)
6176   {
6177     for (j = 1; j <= dynabomb_size; j++)
6178     {
6179       int x = ex + j * xy[i][0];
6180       int y = ey + j * xy[i][1];
6181       int element;
6182
6183       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6184         break;
6185
6186       element = Feld[x][y];
6187
6188       /* do not restart explosions of fields with active bombs */
6189       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6190         continue;
6191
6192       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6193
6194       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6195           !IS_DIGGABLE(element) && !dynabomb_xl)
6196         break;
6197     }
6198   }
6199 }
6200
6201 void Bang(int x, int y)
6202 {
6203   int element = MovingOrBlocked2Element(x, y);
6204   int explosion_type = EX_TYPE_NORMAL;
6205
6206   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6207   {
6208     struct PlayerInfo *player = PLAYERINFO(x, y);
6209
6210 #if USE_FIX_CE_ACTION_WITH_PLAYER
6211     element = Feld[x][y] = player->initial_element;
6212 #else
6213     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6214                             player->element_nr);
6215 #endif
6216
6217     if (level.use_explosion_element[player->index_nr])
6218     {
6219       int explosion_element = level.explosion_element[player->index_nr];
6220
6221       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6222         explosion_type = EX_TYPE_CROSS;
6223       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6224         explosion_type = EX_TYPE_CENTER;
6225     }
6226   }
6227
6228   switch (element)
6229   {
6230     case EL_BUG:
6231     case EL_SPACESHIP:
6232     case EL_BD_BUTTERFLY:
6233     case EL_BD_FIREFLY:
6234     case EL_YAMYAM:
6235     case EL_DARK_YAMYAM:
6236     case EL_ROBOT:
6237     case EL_PACMAN:
6238     case EL_MOLE:
6239       RaiseScoreElement(element);
6240       break;
6241
6242     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6243     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6244     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6245     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6246     case EL_DYNABOMB_INCREASE_NUMBER:
6247     case EL_DYNABOMB_INCREASE_SIZE:
6248     case EL_DYNABOMB_INCREASE_POWER:
6249       explosion_type = EX_TYPE_DYNA;
6250       break;
6251
6252     case EL_DC_LANDMINE:
6253 #if 0
6254     case EL_EM_EXIT_OPEN:
6255     case EL_EM_STEEL_EXIT_OPEN:
6256 #endif
6257       explosion_type = EX_TYPE_CENTER;
6258       break;
6259
6260     case EL_PENGUIN:
6261     case EL_LAMP:
6262     case EL_LAMP_ACTIVE:
6263     case EL_AMOEBA_TO_DIAMOND:
6264       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6265         explosion_type = EX_TYPE_CENTER;
6266       break;
6267
6268     default:
6269       if (element_info[element].explosion_type == EXPLODES_CROSS)
6270         explosion_type = EX_TYPE_CROSS;
6271       else if (element_info[element].explosion_type == EXPLODES_1X1)
6272         explosion_type = EX_TYPE_CENTER;
6273       break;
6274   }
6275
6276   if (explosion_type == EX_TYPE_DYNA)
6277     DynaExplode(x, y);
6278   else
6279     Explode(x, y, EX_PHASE_START, explosion_type);
6280
6281   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6282 }
6283
6284 void SplashAcid(int x, int y)
6285 {
6286   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6287       (!IN_LEV_FIELD(x - 1, y - 2) ||
6288        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6289     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6290
6291   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6292       (!IN_LEV_FIELD(x + 1, y - 2) ||
6293        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6294     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6295
6296   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6297 }
6298
6299 static void InitBeltMovement()
6300 {
6301   static int belt_base_element[4] =
6302   {
6303     EL_CONVEYOR_BELT_1_LEFT,
6304     EL_CONVEYOR_BELT_2_LEFT,
6305     EL_CONVEYOR_BELT_3_LEFT,
6306     EL_CONVEYOR_BELT_4_LEFT
6307   };
6308   static int belt_base_active_element[4] =
6309   {
6310     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6311     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6312     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6313     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6314   };
6315
6316   int x, y, i, j;
6317
6318   /* set frame order for belt animation graphic according to belt direction */
6319   for (i = 0; i < NUM_BELTS; i++)
6320   {
6321     int belt_nr = i;
6322
6323     for (j = 0; j < NUM_BELT_PARTS; j++)
6324     {
6325       int element = belt_base_active_element[belt_nr] + j;
6326       int graphic_1 = el2img(element);
6327       int graphic_2 = el2panelimg(element);
6328
6329       if (game.belt_dir[i] == MV_LEFT)
6330       {
6331         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6332         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6333       }
6334       else
6335       {
6336         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6337         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6338       }
6339     }
6340   }
6341
6342   SCAN_PLAYFIELD(x, y)
6343   {
6344     int element = Feld[x][y];
6345
6346     for (i = 0; i < NUM_BELTS; i++)
6347     {
6348       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6349       {
6350         int e_belt_nr = getBeltNrFromBeltElement(element);
6351         int belt_nr = i;
6352
6353         if (e_belt_nr == belt_nr)
6354         {
6355           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6356
6357           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6358         }
6359       }
6360     }
6361   }
6362 }
6363
6364 static void ToggleBeltSwitch(int x, int y)
6365 {
6366   static int belt_base_element[4] =
6367   {
6368     EL_CONVEYOR_BELT_1_LEFT,
6369     EL_CONVEYOR_BELT_2_LEFT,
6370     EL_CONVEYOR_BELT_3_LEFT,
6371     EL_CONVEYOR_BELT_4_LEFT
6372   };
6373   static int belt_base_active_element[4] =
6374   {
6375     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6376     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6377     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6378     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6379   };
6380   static int belt_base_switch_element[4] =
6381   {
6382     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6383     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6384     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6385     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6386   };
6387   static int belt_move_dir[4] =
6388   {
6389     MV_LEFT,
6390     MV_NONE,
6391     MV_RIGHT,
6392     MV_NONE,
6393   };
6394
6395   int element = Feld[x][y];
6396   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6397   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6398   int belt_dir = belt_move_dir[belt_dir_nr];
6399   int xx, yy, i;
6400
6401   if (!IS_BELT_SWITCH(element))
6402     return;
6403
6404   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6405   game.belt_dir[belt_nr] = belt_dir;
6406
6407   if (belt_dir_nr == 3)
6408     belt_dir_nr = 1;
6409
6410   /* set frame order for belt animation graphic according to belt direction */
6411   for (i = 0; i < NUM_BELT_PARTS; i++)
6412   {
6413     int element = belt_base_active_element[belt_nr] + i;
6414     int graphic_1 = el2img(element);
6415     int graphic_2 = el2panelimg(element);
6416
6417     if (belt_dir == MV_LEFT)
6418     {
6419       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6420       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6421     }
6422     else
6423     {
6424       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6425       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6426     }
6427   }
6428
6429   SCAN_PLAYFIELD(xx, yy)
6430   {
6431     int element = Feld[xx][yy];
6432
6433     if (IS_BELT_SWITCH(element))
6434     {
6435       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6436
6437       if (e_belt_nr == belt_nr)
6438       {
6439         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6440         TEST_DrawLevelField(xx, yy);
6441       }
6442     }
6443     else if (IS_BELT(element) && belt_dir != MV_NONE)
6444     {
6445       int e_belt_nr = getBeltNrFromBeltElement(element);
6446
6447       if (e_belt_nr == belt_nr)
6448       {
6449         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6450
6451         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6452         TEST_DrawLevelField(xx, yy);
6453       }
6454     }
6455     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6456     {
6457       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6458
6459       if (e_belt_nr == belt_nr)
6460       {
6461         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6462
6463         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6464         TEST_DrawLevelField(xx, yy);
6465       }
6466     }
6467   }
6468 }
6469
6470 static void ToggleSwitchgateSwitch(int x, int y)
6471 {
6472   int xx, yy;
6473
6474   game.switchgate_pos = !game.switchgate_pos;
6475
6476   SCAN_PLAYFIELD(xx, yy)
6477   {
6478     int element = Feld[xx][yy];
6479
6480 #if !USE_BOTH_SWITCHGATE_SWITCHES
6481     if (element == EL_SWITCHGATE_SWITCH_UP ||
6482         element == EL_SWITCHGATE_SWITCH_DOWN)
6483     {
6484       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6485       TEST_DrawLevelField(xx, yy);
6486     }
6487     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6488              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6489     {
6490       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6491       TEST_DrawLevelField(xx, yy);
6492     }
6493 #else
6494     if (element == EL_SWITCHGATE_SWITCH_UP)
6495     {
6496       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6497       TEST_DrawLevelField(xx, yy);
6498     }
6499     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6500     {
6501       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6502       TEST_DrawLevelField(xx, yy);
6503     }
6504     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6505     {
6506       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6507       TEST_DrawLevelField(xx, yy);
6508     }
6509     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6510     {
6511       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6512       TEST_DrawLevelField(xx, yy);
6513     }
6514 #endif
6515     else if (element == EL_SWITCHGATE_OPEN ||
6516              element == EL_SWITCHGATE_OPENING)
6517     {
6518       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6519
6520       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6521     }
6522     else if (element == EL_SWITCHGATE_CLOSED ||
6523              element == EL_SWITCHGATE_CLOSING)
6524     {
6525       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6526
6527       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6528     }
6529   }
6530 }
6531
6532 static int getInvisibleActiveFromInvisibleElement(int element)
6533 {
6534   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6535           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6536           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6537           element);
6538 }
6539
6540 static int getInvisibleFromInvisibleActiveElement(int element)
6541 {
6542   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6543           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6544           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6545           element);
6546 }
6547
6548 static void RedrawAllLightSwitchesAndInvisibleElements()
6549 {
6550   int x, y;
6551
6552   SCAN_PLAYFIELD(x, y)
6553   {
6554     int element = Feld[x][y];
6555
6556     if (element == EL_LIGHT_SWITCH &&
6557         game.light_time_left > 0)
6558     {
6559       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6560       TEST_DrawLevelField(x, y);
6561     }
6562     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6563              game.light_time_left == 0)
6564     {
6565       Feld[x][y] = EL_LIGHT_SWITCH;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_DRIPPER &&
6569              game.light_time_left > 0)
6570     {
6571       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6575              game.light_time_left == 0)
6576     {
6577       Feld[x][y] = EL_EMC_DRIPPER;
6578       TEST_DrawLevelField(x, y);
6579     }
6580     else if (element == EL_INVISIBLE_STEELWALL ||
6581              element == EL_INVISIBLE_WALL ||
6582              element == EL_INVISIBLE_SAND)
6583     {
6584       if (game.light_time_left > 0)
6585         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6586
6587       TEST_DrawLevelField(x, y);
6588
6589       /* uncrumble neighbour fields, if needed */
6590       if (element == EL_INVISIBLE_SAND)
6591         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6592     }
6593     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6594              element == EL_INVISIBLE_WALL_ACTIVE ||
6595              element == EL_INVISIBLE_SAND_ACTIVE)
6596     {
6597       if (game.light_time_left == 0)
6598         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6599
6600       TEST_DrawLevelField(x, y);
6601
6602       /* re-crumble neighbour fields, if needed */
6603       if (element == EL_INVISIBLE_SAND)
6604         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6605     }
6606   }
6607 }
6608
6609 static void RedrawAllInvisibleElementsForLenses()
6610 {
6611   int x, y;
6612
6613   SCAN_PLAYFIELD(x, y)
6614   {
6615     int element = Feld[x][y];
6616
6617     if (element == EL_EMC_DRIPPER &&
6618         game.lenses_time_left > 0)
6619     {
6620       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6621       TEST_DrawLevelField(x, y);
6622     }
6623     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6624              game.lenses_time_left == 0)
6625     {
6626       Feld[x][y] = EL_EMC_DRIPPER;
6627       TEST_DrawLevelField(x, y);
6628     }
6629     else if (element == EL_INVISIBLE_STEELWALL ||
6630              element == EL_INVISIBLE_WALL ||
6631              element == EL_INVISIBLE_SAND)
6632     {
6633       if (game.lenses_time_left > 0)
6634         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6635
6636       TEST_DrawLevelField(x, y);
6637
6638       /* uncrumble neighbour fields, if needed */
6639       if (element == EL_INVISIBLE_SAND)
6640         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6641     }
6642     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6643              element == EL_INVISIBLE_WALL_ACTIVE ||
6644              element == EL_INVISIBLE_SAND_ACTIVE)
6645     {
6646       if (game.lenses_time_left == 0)
6647         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6648
6649       TEST_DrawLevelField(x, y);
6650
6651       /* re-crumble neighbour fields, if needed */
6652       if (element == EL_INVISIBLE_SAND)
6653         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6654     }
6655   }
6656 }
6657
6658 static void RedrawAllInvisibleElementsForMagnifier()
6659 {
6660   int x, y;
6661
6662   SCAN_PLAYFIELD(x, y)
6663   {
6664     int element = Feld[x][y];
6665
6666     if (element == EL_EMC_FAKE_GRASS &&
6667         game.magnify_time_left > 0)
6668     {
6669       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6670       TEST_DrawLevelField(x, y);
6671     }
6672     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6673              game.magnify_time_left == 0)
6674     {
6675       Feld[x][y] = EL_EMC_FAKE_GRASS;
6676       TEST_DrawLevelField(x, y);
6677     }
6678     else if (IS_GATE_GRAY(element) &&
6679              game.magnify_time_left > 0)
6680     {
6681       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6682                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6683                     IS_EM_GATE_GRAY(element) ?
6684                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6685                     IS_EMC_GATE_GRAY(element) ?
6686                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6687                     IS_DC_GATE_GRAY(element) ?
6688                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6689                     element);
6690       TEST_DrawLevelField(x, y);
6691     }
6692     else if (IS_GATE_GRAY_ACTIVE(element) &&
6693              game.magnify_time_left == 0)
6694     {
6695       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6696                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6697                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6698                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6699                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6700                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6701                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6702                     EL_DC_GATE_WHITE_GRAY :
6703                     element);
6704       TEST_DrawLevelField(x, y);
6705     }
6706   }
6707 }
6708
6709 static void ToggleLightSwitch(int x, int y)
6710 {
6711   int element = Feld[x][y];
6712
6713   game.light_time_left =
6714     (element == EL_LIGHT_SWITCH ?
6715      level.time_light * FRAMES_PER_SECOND : 0);
6716
6717   RedrawAllLightSwitchesAndInvisibleElements();
6718 }
6719
6720 static void ActivateTimegateSwitch(int x, int y)
6721 {
6722   int xx, yy;
6723
6724   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6725
6726   SCAN_PLAYFIELD(xx, yy)
6727   {
6728     int element = Feld[xx][yy];
6729
6730     if (element == EL_TIMEGATE_CLOSED ||
6731         element == EL_TIMEGATE_CLOSING)
6732     {
6733       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6734       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6735     }
6736
6737     /*
6738     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6739     {
6740       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6741       TEST_DrawLevelField(xx, yy);
6742     }
6743     */
6744
6745   }
6746
6747 #if 1
6748   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6749                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6750 #else
6751   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6752 #endif
6753 }
6754
6755 void Impact(int x, int y)
6756 {
6757   boolean last_line = (y == lev_fieldy - 1);
6758   boolean object_hit = FALSE;
6759   boolean impact = (last_line || object_hit);
6760   int element = Feld[x][y];
6761   int smashed = EL_STEELWALL;
6762
6763   if (!last_line)       /* check if element below was hit */
6764   {
6765     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6766       return;
6767
6768     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6769                                          MovDir[x][y + 1] != MV_DOWN ||
6770                                          MovPos[x][y + 1] <= TILEY / 2));
6771
6772     /* do not smash moving elements that left the smashed field in time */
6773     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6774         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6775       object_hit = FALSE;
6776
6777 #if USE_QUICKSAND_IMPACT_BUGFIX
6778     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6779     {
6780       RemoveMovingField(x, y + 1);
6781       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6782       Feld[x][y + 2] = EL_ROCK;
6783       TEST_DrawLevelField(x, y + 2);
6784
6785       object_hit = TRUE;
6786     }
6787
6788     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6789     {
6790       RemoveMovingField(x, y + 1);
6791       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6792       Feld[x][y + 2] = EL_ROCK;
6793       TEST_DrawLevelField(x, y + 2);
6794
6795       object_hit = TRUE;
6796     }
6797 #endif
6798
6799     if (object_hit)
6800       smashed = MovingOrBlocked2Element(x, y + 1);
6801
6802     impact = (last_line || object_hit);
6803   }
6804
6805   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6806   {
6807     SplashAcid(x, y + 1);
6808     return;
6809   }
6810
6811   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6812   /* only reset graphic animation if graphic really changes after impact */
6813   if (impact &&
6814       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6815   {
6816     ResetGfxAnimation(x, y);
6817     TEST_DrawLevelField(x, y);
6818   }
6819
6820   if (impact && CAN_EXPLODE_IMPACT(element))
6821   {
6822     Bang(x, y);
6823     return;
6824   }
6825   else if (impact && element == EL_PEARL &&
6826            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6827   {
6828     ResetGfxAnimation(x, y);
6829
6830     Feld[x][y] = EL_PEARL_BREAKING;
6831     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6832     return;
6833   }
6834   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6835   {
6836     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6837
6838     return;
6839   }
6840
6841   if (impact && element == EL_AMOEBA_DROP)
6842   {
6843     if (object_hit && IS_PLAYER(x, y + 1))
6844       KillPlayerUnlessEnemyProtected(x, y + 1);
6845     else if (object_hit && smashed == EL_PENGUIN)
6846       Bang(x, y + 1);
6847     else
6848     {
6849       Feld[x][y] = EL_AMOEBA_GROWING;
6850       Store[x][y] = EL_AMOEBA_WET;
6851
6852       ResetRandomAnimationValue(x, y);
6853     }
6854     return;
6855   }
6856
6857   if (object_hit)               /* check which object was hit */
6858   {
6859     if ((CAN_PASS_MAGIC_WALL(element) && 
6860          (smashed == EL_MAGIC_WALL ||
6861           smashed == EL_BD_MAGIC_WALL)) ||
6862         (CAN_PASS_DC_MAGIC_WALL(element) &&
6863          smashed == EL_DC_MAGIC_WALL))
6864     {
6865       int xx, yy;
6866       int activated_magic_wall =
6867         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6868          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6869          EL_DC_MAGIC_WALL_ACTIVE);
6870
6871       /* activate magic wall / mill */
6872       SCAN_PLAYFIELD(xx, yy)
6873       {
6874         if (Feld[xx][yy] == smashed)
6875           Feld[xx][yy] = activated_magic_wall;
6876       }
6877
6878       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6879       game.magic_wall_active = TRUE;
6880
6881       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6882                             SND_MAGIC_WALL_ACTIVATING :
6883                             smashed == EL_BD_MAGIC_WALL ?
6884                             SND_BD_MAGIC_WALL_ACTIVATING :
6885                             SND_DC_MAGIC_WALL_ACTIVATING));
6886     }
6887
6888     if (IS_PLAYER(x, y + 1))
6889     {
6890       if (CAN_SMASH_PLAYER(element))
6891       {
6892         KillPlayerUnlessEnemyProtected(x, y + 1);
6893         return;
6894       }
6895     }
6896     else if (smashed == EL_PENGUIN)
6897     {
6898       if (CAN_SMASH_PLAYER(element))
6899       {
6900         Bang(x, y + 1);
6901         return;
6902       }
6903     }
6904     else if (element == EL_BD_DIAMOND)
6905     {
6906       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6907       {
6908         Bang(x, y + 1);
6909         return;
6910       }
6911     }
6912     else if (((element == EL_SP_INFOTRON ||
6913                element == EL_SP_ZONK) &&
6914               (smashed == EL_SP_SNIKSNAK ||
6915                smashed == EL_SP_ELECTRON ||
6916                smashed == EL_SP_DISK_ORANGE)) ||
6917              (element == EL_SP_INFOTRON &&
6918               smashed == EL_SP_DISK_YELLOW))
6919     {
6920       Bang(x, y + 1);
6921       return;
6922     }
6923     else if (CAN_SMASH_EVERYTHING(element))
6924     {
6925       if (IS_CLASSIC_ENEMY(smashed) ||
6926           CAN_EXPLODE_SMASHED(smashed))
6927       {
6928         Bang(x, y + 1);
6929         return;
6930       }
6931       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6932       {
6933         if (smashed == EL_LAMP ||
6934             smashed == EL_LAMP_ACTIVE)
6935         {
6936           Bang(x, y + 1);
6937           return;
6938         }
6939         else if (smashed == EL_NUT)
6940         {
6941           Feld[x][y + 1] = EL_NUT_BREAKING;
6942           PlayLevelSound(x, y, SND_NUT_BREAKING);
6943           RaiseScoreElement(EL_NUT);
6944           return;
6945         }
6946         else if (smashed == EL_PEARL)
6947         {
6948           ResetGfxAnimation(x, y);
6949
6950           Feld[x][y + 1] = EL_PEARL_BREAKING;
6951           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6952           return;
6953         }
6954         else if (smashed == EL_DIAMOND)
6955         {
6956           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6957           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6958           return;
6959         }
6960         else if (IS_BELT_SWITCH(smashed))
6961         {
6962           ToggleBeltSwitch(x, y + 1);
6963         }
6964         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6965                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6966                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6967                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6968         {
6969           ToggleSwitchgateSwitch(x, y + 1);
6970         }
6971         else if (smashed == EL_LIGHT_SWITCH ||
6972                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6973         {
6974           ToggleLightSwitch(x, y + 1);
6975         }
6976         else
6977         {
6978 #if 0
6979           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6980 #endif
6981
6982           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6983
6984           CheckElementChangeBySide(x, y + 1, smashed, element,
6985                                    CE_SWITCHED, CH_SIDE_TOP);
6986           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6987                                             CH_SIDE_TOP);
6988         }
6989       }
6990       else
6991       {
6992         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6993       }
6994     }
6995   }
6996
6997   /* play sound of magic wall / mill */
6998   if (!last_line &&
6999       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7000        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7001        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7002   {
7003     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7004       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7005     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7006       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7007     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7008       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7009
7010     return;
7011   }
7012
7013   /* play sound of object that hits the ground */
7014   if (last_line || object_hit)
7015     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7016 }
7017
7018 inline static void TurnRoundExt(int x, int y)
7019 {
7020   static struct
7021   {
7022     int dx, dy;
7023   } move_xy[] =
7024   {
7025     {  0,  0 },
7026     { -1,  0 },
7027     { +1,  0 },
7028     {  0,  0 },
7029     {  0, -1 },
7030     {  0,  0 }, { 0, 0 }, { 0, 0 },
7031     {  0, +1 }
7032   };
7033   static struct
7034   {
7035     int left, right, back;
7036   } turn[] =
7037   {
7038     { 0,        0,              0        },
7039     { MV_DOWN,  MV_UP,          MV_RIGHT },
7040     { MV_UP,    MV_DOWN,        MV_LEFT  },
7041     { 0,        0,              0        },
7042     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7043     { 0,        0,              0        },
7044     { 0,        0,              0        },
7045     { 0,        0,              0        },
7046     { MV_RIGHT, MV_LEFT,        MV_UP    }
7047   };
7048
7049   int element = Feld[x][y];
7050   int move_pattern = element_info[element].move_pattern;
7051
7052   int old_move_dir = MovDir[x][y];
7053   int left_dir  = turn[old_move_dir].left;
7054   int right_dir = turn[old_move_dir].right;
7055   int back_dir  = turn[old_move_dir].back;
7056
7057   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7058   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7059   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7060   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7061
7062   int left_x  = x + left_dx,  left_y  = y + left_dy;
7063   int right_x = x + right_dx, right_y = y + right_dy;
7064   int move_x  = x + move_dx,  move_y  = y + move_dy;
7065
7066   int xx, yy;
7067
7068   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7069   {
7070     TestIfBadThingTouchesOtherBadThing(x, y);
7071
7072     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7073       MovDir[x][y] = right_dir;
7074     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7075       MovDir[x][y] = left_dir;
7076
7077     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7078       MovDelay[x][y] = 9;
7079     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7080       MovDelay[x][y] = 1;
7081   }
7082   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7083   {
7084     TestIfBadThingTouchesOtherBadThing(x, y);
7085
7086     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7087       MovDir[x][y] = left_dir;
7088     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7089       MovDir[x][y] = right_dir;
7090
7091     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7092       MovDelay[x][y] = 9;
7093     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7094       MovDelay[x][y] = 1;
7095   }
7096   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7097   {
7098     TestIfBadThingTouchesOtherBadThing(x, y);
7099
7100     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7101       MovDir[x][y] = left_dir;
7102     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7103       MovDir[x][y] = right_dir;
7104
7105     if (MovDir[x][y] != old_move_dir)
7106       MovDelay[x][y] = 9;
7107   }
7108   else if (element == EL_YAMYAM)
7109   {
7110     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7111     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7112
7113     if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7115     else if (can_turn_left)
7116       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7117     else if (can_turn_right)
7118       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7119     else
7120       MovDir[x][y] = back_dir;
7121
7122     MovDelay[x][y] = 16 + 16 * RND(3);
7123   }
7124   else if (element == EL_DARK_YAMYAM)
7125   {
7126     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7127                                                          left_x, left_y);
7128     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7129                                                          right_x, right_y);
7130
7131     if (can_turn_left && can_turn_right)
7132       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7133     else if (can_turn_left)
7134       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7135     else if (can_turn_right)
7136       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7137     else
7138       MovDir[x][y] = back_dir;
7139
7140     MovDelay[x][y] = 16 + 16 * RND(3);
7141   }
7142   else if (element == EL_PACMAN)
7143   {
7144     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7145     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7146
7147     if (can_turn_left && can_turn_right)
7148       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149     else if (can_turn_left)
7150       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151     else if (can_turn_right)
7152       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7153     else
7154       MovDir[x][y] = back_dir;
7155
7156     MovDelay[x][y] = 6 + RND(40);
7157   }
7158   else if (element == EL_PIG)
7159   {
7160     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7161     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7162     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7163     boolean should_turn_left, should_turn_right, should_move_on;
7164     int rnd_value = 24;
7165     int rnd = RND(rnd_value);
7166
7167     should_turn_left = (can_turn_left &&
7168                         (!can_move_on ||
7169                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7170                                                    y + back_dy + left_dy)));
7171     should_turn_right = (can_turn_right &&
7172                          (!can_move_on ||
7173                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7174                                                     y + back_dy + right_dy)));
7175     should_move_on = (can_move_on &&
7176                       (!can_turn_left ||
7177                        !can_turn_right ||
7178                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7179                                                  y + move_dy + left_dy) ||
7180                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7181                                                  y + move_dy + right_dy)));
7182
7183     if (should_turn_left || should_turn_right || should_move_on)
7184     {
7185       if (should_turn_left && should_turn_right && should_move_on)
7186         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7187                         rnd < 2 * rnd_value / 3 ? right_dir :
7188                         old_move_dir);
7189       else if (should_turn_left && should_turn_right)
7190         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7191       else if (should_turn_left && should_move_on)
7192         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7193       else if (should_turn_right && should_move_on)
7194         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7195       else if (should_turn_left)
7196         MovDir[x][y] = left_dir;
7197       else if (should_turn_right)
7198         MovDir[x][y] = right_dir;
7199       else if (should_move_on)
7200         MovDir[x][y] = old_move_dir;
7201     }
7202     else if (can_move_on && rnd > rnd_value / 8)
7203       MovDir[x][y] = old_move_dir;
7204     else if (can_turn_left && can_turn_right)
7205       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7206     else if (can_turn_left && rnd > rnd_value / 8)
7207       MovDir[x][y] = left_dir;
7208     else if (can_turn_right && rnd > rnd_value/8)
7209       MovDir[x][y] = right_dir;
7210     else
7211       MovDir[x][y] = back_dir;
7212
7213     xx = x + move_xy[MovDir[x][y]].dx;
7214     yy = y + move_xy[MovDir[x][y]].dy;
7215
7216     if (!IN_LEV_FIELD(xx, yy) ||
7217         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7218       MovDir[x][y] = old_move_dir;
7219
7220     MovDelay[x][y] = 0;
7221   }
7222   else if (element == EL_DRAGON)
7223   {
7224     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7225     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7226     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7227     int rnd_value = 24;
7228     int rnd = RND(rnd_value);
7229
7230     if (can_move_on && rnd > rnd_value / 8)
7231       MovDir[x][y] = old_move_dir;
7232     else if (can_turn_left && can_turn_right)
7233       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7234     else if (can_turn_left && rnd > rnd_value / 8)
7235       MovDir[x][y] = left_dir;
7236     else if (can_turn_right && rnd > rnd_value / 8)
7237       MovDir[x][y] = right_dir;
7238     else
7239       MovDir[x][y] = back_dir;
7240
7241     xx = x + move_xy[MovDir[x][y]].dx;
7242     yy = y + move_xy[MovDir[x][y]].dy;
7243
7244     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7245       MovDir[x][y] = old_move_dir;
7246
7247     MovDelay[x][y] = 0;
7248   }
7249   else if (element == EL_MOLE)
7250   {
7251     boolean can_move_on =
7252       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7253                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7254                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7255     if (!can_move_on)
7256     {
7257       boolean can_turn_left =
7258         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7259                               IS_AMOEBOID(Feld[left_x][left_y])));
7260
7261       boolean can_turn_right =
7262         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7263                               IS_AMOEBOID(Feld[right_x][right_y])));
7264
7265       if (can_turn_left && can_turn_right)
7266         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7267       else if (can_turn_left)
7268         MovDir[x][y] = left_dir;
7269       else
7270         MovDir[x][y] = right_dir;
7271     }
7272
7273     if (MovDir[x][y] != old_move_dir)
7274       MovDelay[x][y] = 9;
7275   }
7276   else if (element == EL_BALLOON)
7277   {
7278     MovDir[x][y] = game.wind_direction;
7279     MovDelay[x][y] = 0;
7280   }
7281   else if (element == EL_SPRING)
7282   {
7283 #if USE_NEW_SPRING_BUMPER
7284     if (MovDir[x][y] & MV_HORIZONTAL)
7285     {
7286       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7287           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7288       {
7289         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7290         ResetGfxAnimation(move_x, move_y);
7291         TEST_DrawLevelField(move_x, move_y);
7292
7293         MovDir[x][y] = back_dir;
7294       }
7295       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7296                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7297         MovDir[x][y] = MV_NONE;
7298     }
7299 #else
7300     if (MovDir[x][y] & MV_HORIZONTAL &&
7301         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7302          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7303       MovDir[x][y] = MV_NONE;
7304 #endif
7305
7306     MovDelay[x][y] = 0;
7307   }
7308   else if (element == EL_ROBOT ||
7309            element == EL_SATELLITE ||
7310            element == EL_PENGUIN ||
7311            element == EL_EMC_ANDROID)
7312   {
7313     int attr_x = -1, attr_y = -1;
7314
7315     if (AllPlayersGone)
7316     {
7317       attr_x = ExitX;
7318       attr_y = ExitY;
7319     }
7320     else
7321     {
7322       int i;
7323
7324       for (i = 0; i < MAX_PLAYERS; i++)
7325       {
7326         struct PlayerInfo *player = &stored_player[i];
7327         int jx = player->jx, jy = player->jy;
7328
7329         if (!player->active)
7330           continue;
7331
7332         if (attr_x == -1 ||
7333             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7334         {
7335           attr_x = jx;
7336           attr_y = jy;
7337         }
7338       }
7339     }
7340
7341     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7342         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7343          game.engine_version < VERSION_IDENT(3,1,0,0)))
7344     {
7345       attr_x = ZX;
7346       attr_y = ZY;
7347     }
7348
7349     if (element == EL_PENGUIN)
7350     {
7351       int i;
7352       static int xy[4][2] =
7353       {
7354         { 0, -1 },
7355         { -1, 0 },
7356         { +1, 0 },
7357         { 0, +1 }
7358       };
7359
7360       for (i = 0; i < NUM_DIRECTIONS; i++)
7361       {
7362         int ex = x + xy[i][0];
7363         int ey = y + xy[i][1];
7364
7365         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7366                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7367                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7368                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7369         {
7370           attr_x = ex;
7371           attr_y = ey;
7372           break;
7373         }
7374       }
7375     }
7376
7377     MovDir[x][y] = MV_NONE;
7378     if (attr_x < x)
7379       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7380     else if (attr_x > x)
7381       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7382     if (attr_y < y)
7383       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7384     else if (attr_y > y)
7385       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7386
7387     if (element == EL_ROBOT)
7388     {
7389       int newx, newy;
7390
7391       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7392         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7393       Moving2Blocked(x, y, &newx, &newy);
7394
7395       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7396         MovDelay[x][y] = 8 + 8 * !RND(3);
7397       else
7398         MovDelay[x][y] = 16;
7399     }
7400     else if (element == EL_PENGUIN)
7401     {
7402       int newx, newy;
7403
7404       MovDelay[x][y] = 1;
7405
7406       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7407       {
7408         boolean first_horiz = RND(2);
7409         int new_move_dir = MovDir[x][y];
7410
7411         MovDir[x][y] =
7412           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7413         Moving2Blocked(x, y, &newx, &newy);
7414
7415         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7416           return;
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] = old_move_dir;
7426         return;
7427       }
7428     }
7429     else if (element == EL_SATELLITE)
7430     {
7431       int newx, newy;
7432
7433       MovDelay[x][y] = 1;
7434
7435       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7436       {
7437         boolean first_horiz = RND(2);
7438         int new_move_dir = MovDir[x][y];
7439
7440         MovDir[x][y] =
7441           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7442         Moving2Blocked(x, y, &newx, &newy);
7443
7444         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7445           return;
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] = old_move_dir;
7455         return;
7456       }
7457     }
7458     else if (element == EL_EMC_ANDROID)
7459     {
7460       static int check_pos[16] =
7461       {
7462         -1,             /*  0 => (invalid)          */
7463         7,              /*  1 => MV_LEFT            */
7464         3,              /*  2 => MV_RIGHT           */
7465         -1,             /*  3 => (invalid)          */
7466         1,              /*  4 =>            MV_UP   */
7467         0,              /*  5 => MV_LEFT  | MV_UP   */
7468         2,              /*  6 => MV_RIGHT | MV_UP   */
7469         -1,             /*  7 => (invalid)          */
7470         5,              /*  8 =>            MV_DOWN */
7471         6,              /*  9 => MV_LEFT  | MV_DOWN */
7472         4,              /* 10 => MV_RIGHT | MV_DOWN */
7473         -1,             /* 11 => (invalid)          */
7474         -1,             /* 12 => (invalid)          */
7475         -1,             /* 13 => (invalid)          */
7476         -1,             /* 14 => (invalid)          */
7477         -1,             /* 15 => (invalid)          */
7478       };
7479       static struct
7480       {
7481         int dx, dy;
7482         int dir;
7483       } check_xy[8] =
7484       {
7485         { -1, -1,       MV_LEFT  | MV_UP   },
7486         {  0, -1,                  MV_UP   },
7487         { +1, -1,       MV_RIGHT | MV_UP   },
7488         { +1,  0,       MV_RIGHT           },
7489         { +1, +1,       MV_RIGHT | MV_DOWN },
7490         {  0, +1,                  MV_DOWN },
7491         { -1, +1,       MV_LEFT  | MV_DOWN },
7492         { -1,  0,       MV_LEFT            },
7493       };
7494       int start_pos, check_order;
7495       boolean can_clone = FALSE;
7496       int i;
7497
7498       /* check if there is any free field around current position */
7499       for (i = 0; i < 8; i++)
7500       {
7501         int newx = x + check_xy[i].dx;
7502         int newy = y + check_xy[i].dy;
7503
7504         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7505         {
7506           can_clone = TRUE;
7507
7508           break;
7509         }
7510       }
7511
7512       if (can_clone)            /* randomly find an element to clone */
7513       {
7514         can_clone = FALSE;
7515
7516         start_pos = check_pos[RND(8)];
7517         check_order = (RND(2) ? -1 : +1);
7518
7519         for (i = 0; i < 8; i++)
7520         {
7521           int pos_raw = start_pos + i * check_order;
7522           int pos = (pos_raw + 8) % 8;
7523           int newx = x + check_xy[pos].dx;
7524           int newy = y + check_xy[pos].dy;
7525
7526           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7527           {
7528             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7529             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7530
7531             Store[x][y] = Feld[newx][newy];
7532
7533             can_clone = TRUE;
7534
7535             break;
7536           }
7537         }
7538       }
7539
7540       if (can_clone)            /* randomly find a direction to move */
7541       {
7542         can_clone = FALSE;
7543
7544         start_pos = check_pos[RND(8)];
7545         check_order = (RND(2) ? -1 : +1);
7546
7547         for (i = 0; i < 8; i++)
7548         {
7549           int pos_raw = start_pos + i * check_order;
7550           int pos = (pos_raw + 8) % 8;
7551           int newx = x + check_xy[pos].dx;
7552           int newy = y + check_xy[pos].dy;
7553           int new_move_dir = check_xy[pos].dir;
7554
7555           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7556           {
7557             MovDir[x][y] = new_move_dir;
7558             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7559
7560             can_clone = TRUE;
7561
7562             break;
7563           }
7564         }
7565       }
7566
7567       if (can_clone)            /* cloning and moving successful */
7568         return;
7569
7570       /* cannot clone -- try to move towards player */
7571
7572       start_pos = check_pos[MovDir[x][y] & 0x0f];
7573       check_order = (RND(2) ? -1 : +1);
7574
7575       for (i = 0; i < 3; i++)
7576       {
7577         /* first check start_pos, then previous/next or (next/previous) pos */
7578         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7579         int pos = (pos_raw + 8) % 8;
7580         int newx = x + check_xy[pos].dx;
7581         int newy = y + check_xy[pos].dy;
7582         int new_move_dir = check_xy[pos].dir;
7583
7584         if (IS_PLAYER(newx, newy))
7585           break;
7586
7587         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7588         {
7589           MovDir[x][y] = new_move_dir;
7590           MovDelay[x][y] = level.android_move_time * 8 + 1;
7591
7592           break;
7593         }
7594       }
7595     }
7596   }
7597   else if (move_pattern == MV_TURNING_LEFT ||
7598            move_pattern == MV_TURNING_RIGHT ||
7599            move_pattern == MV_TURNING_LEFT_RIGHT ||
7600            move_pattern == MV_TURNING_RIGHT_LEFT ||
7601            move_pattern == MV_TURNING_RANDOM ||
7602            move_pattern == MV_ALL_DIRECTIONS)
7603   {
7604     boolean can_turn_left =
7605       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7606     boolean can_turn_right =
7607       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7608
7609     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7610       return;
7611
7612     if (move_pattern == MV_TURNING_LEFT)
7613       MovDir[x][y] = left_dir;
7614     else if (move_pattern == MV_TURNING_RIGHT)
7615       MovDir[x][y] = right_dir;
7616     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7617       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7618     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7619       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7620     else if (move_pattern == MV_TURNING_RANDOM)
7621       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7622                       can_turn_right && !can_turn_left ? right_dir :
7623                       RND(2) ? left_dir : right_dir);
7624     else if (can_turn_left && can_turn_right)
7625       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7626     else if (can_turn_left)
7627       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7628     else if (can_turn_right)
7629       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7630     else
7631       MovDir[x][y] = back_dir;
7632
7633     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7634   }
7635   else if (move_pattern == MV_HORIZONTAL ||
7636            move_pattern == MV_VERTICAL)
7637   {
7638     if (move_pattern & old_move_dir)
7639       MovDir[x][y] = back_dir;
7640     else if (move_pattern == MV_HORIZONTAL)
7641       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7642     else if (move_pattern == MV_VERTICAL)
7643       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7644
7645     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7646   }
7647   else if (move_pattern & MV_ANY_DIRECTION)
7648   {
7649     MovDir[x][y] = move_pattern;
7650     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7651   }
7652   else if (move_pattern & MV_WIND_DIRECTION)
7653   {
7654     MovDir[x][y] = game.wind_direction;
7655     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7656   }
7657   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7658   {
7659     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7660       MovDir[x][y] = left_dir;
7661     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7662       MovDir[x][y] = right_dir;
7663
7664     if (MovDir[x][y] != old_move_dir)
7665       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7666   }
7667   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7668   {
7669     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7670       MovDir[x][y] = right_dir;
7671     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7672       MovDir[x][y] = left_dir;
7673
7674     if (MovDir[x][y] != old_move_dir)
7675       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7676   }
7677   else if (move_pattern == MV_TOWARDS_PLAYER ||
7678            move_pattern == MV_AWAY_FROM_PLAYER)
7679   {
7680     int attr_x = -1, attr_y = -1;
7681     int newx, newy;
7682     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7683
7684     if (AllPlayersGone)
7685     {
7686       attr_x = ExitX;
7687       attr_y = ExitY;
7688     }
7689     else
7690     {
7691       int i;
7692
7693       for (i = 0; i < MAX_PLAYERS; i++)
7694       {
7695         struct PlayerInfo *player = &stored_player[i];
7696         int jx = player->jx, jy = player->jy;
7697
7698         if (!player->active)
7699           continue;
7700
7701         if (attr_x == -1 ||
7702             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7703         {
7704           attr_x = jx;
7705           attr_y = jy;
7706         }
7707       }
7708     }
7709
7710     MovDir[x][y] = MV_NONE;
7711     if (attr_x < x)
7712       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7713     else if (attr_x > x)
7714       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7715     if (attr_y < y)
7716       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7717     else if (attr_y > y)
7718       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7719
7720     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7721
7722     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7723     {
7724       boolean first_horiz = RND(2);
7725       int new_move_dir = MovDir[x][y];
7726
7727       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7728       {
7729         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7730         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7731
7732         return;
7733       }
7734
7735       MovDir[x][y] =
7736         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7737       Moving2Blocked(x, y, &newx, &newy);
7738
7739       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7740         return;
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] = old_move_dir;
7750     }
7751   }
7752   else if (move_pattern == MV_WHEN_PUSHED ||
7753            move_pattern == MV_WHEN_DROPPED)
7754   {
7755     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7756       MovDir[x][y] = MV_NONE;
7757
7758     MovDelay[x][y] = 0;
7759   }
7760   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7761   {
7762     static int test_xy[7][2] =
7763     {
7764       { 0, -1 },
7765       { -1, 0 },
7766       { +1, 0 },
7767       { 0, +1 },
7768       { 0, -1 },
7769       { -1, 0 },
7770       { +1, 0 },
7771     };
7772     static int test_dir[7] =
7773     {
7774       MV_UP,
7775       MV_LEFT,
7776       MV_RIGHT,
7777       MV_DOWN,
7778       MV_UP,
7779       MV_LEFT,
7780       MV_RIGHT,
7781     };
7782     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7783     int move_preference = -1000000;     /* start with very low preference */
7784     int new_move_dir = MV_NONE;
7785     int start_test = RND(4);
7786     int i;
7787
7788     for (i = 0; i < NUM_DIRECTIONS; i++)
7789     {
7790       int move_dir = test_dir[start_test + i];
7791       int move_dir_preference;
7792
7793       xx = x + test_xy[start_test + i][0];
7794       yy = y + test_xy[start_test + i][1];
7795
7796       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7797           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7798       {
7799         new_move_dir = move_dir;
7800
7801         break;
7802       }
7803
7804       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7805         continue;
7806
7807       move_dir_preference = -1 * RunnerVisit[xx][yy];
7808       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7809         move_dir_preference = PlayerVisit[xx][yy];
7810
7811       if (move_dir_preference > move_preference)
7812       {
7813         /* prefer field that has not been visited for the longest time */
7814         move_preference = move_dir_preference;
7815         new_move_dir = move_dir;
7816       }
7817       else if (move_dir_preference == move_preference &&
7818                move_dir == old_move_dir)
7819       {
7820         /* prefer last direction when all directions are preferred equally */
7821         move_preference = move_dir_preference;
7822         new_move_dir = move_dir;
7823       }
7824     }
7825
7826     MovDir[x][y] = new_move_dir;
7827     if (old_move_dir != new_move_dir)
7828       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7829   }
7830 }
7831
7832 static void TurnRound(int x, int y)
7833 {
7834   int direction = MovDir[x][y];
7835
7836   TurnRoundExt(x, y);
7837
7838   GfxDir[x][y] = MovDir[x][y];
7839
7840   if (direction != MovDir[x][y])
7841     GfxFrame[x][y] = 0;
7842
7843   if (MovDelay[x][y])
7844     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7845
7846   ResetGfxFrame(x, y, FALSE);
7847 }
7848
7849 static boolean JustBeingPushed(int x, int y)
7850 {
7851   int i;
7852
7853   for (i = 0; i < MAX_PLAYERS; i++)
7854   {
7855     struct PlayerInfo *player = &stored_player[i];
7856
7857     if (player->active && player->is_pushing && player->MovPos)
7858     {
7859       int next_jx = player->jx + (player->jx - player->last_jx);
7860       int next_jy = player->jy + (player->jy - player->last_jy);
7861
7862       if (x == next_jx && y == next_jy)
7863         return TRUE;
7864     }
7865   }
7866
7867   return FALSE;
7868 }
7869
7870 void StartMoving(int x, int y)
7871 {
7872   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7873   int element = Feld[x][y];
7874
7875   if (Stop[x][y])
7876     return;
7877
7878   if (MovDelay[x][y] == 0)
7879     GfxAction[x][y] = ACTION_DEFAULT;
7880
7881   if (CAN_FALL(element) && y < lev_fieldy - 1)
7882   {
7883     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7884         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7885       if (JustBeingPushed(x, y))
7886         return;
7887
7888     if (element == EL_QUICKSAND_FULL)
7889     {
7890       if (IS_FREE(x, y + 1))
7891       {
7892         InitMovingField(x, y, MV_DOWN);
7893         started_moving = TRUE;
7894
7895         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7896 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7897         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7898           Store[x][y] = EL_ROCK;
7899 #else
7900         Store[x][y] = EL_ROCK;
7901 #endif
7902
7903         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7904       }
7905       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7906       {
7907         if (!MovDelay[x][y])
7908         {
7909           MovDelay[x][y] = TILEY + 1;
7910
7911           ResetGfxAnimation(x, y);
7912           ResetGfxAnimation(x, y + 1);
7913         }
7914
7915         if (MovDelay[x][y])
7916         {
7917           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7918           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7919
7920           MovDelay[x][y]--;
7921           if (MovDelay[x][y])
7922             return;
7923         }
7924
7925         Feld[x][y] = EL_QUICKSAND_EMPTY;
7926         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7927         Store[x][y + 1] = Store[x][y];
7928         Store[x][y] = 0;
7929
7930         PlayLevelSoundAction(x, y, ACTION_FILLING);
7931       }
7932       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7933       {
7934         if (!MovDelay[x][y])
7935         {
7936           MovDelay[x][y] = TILEY + 1;
7937
7938           ResetGfxAnimation(x, y);
7939           ResetGfxAnimation(x, y + 1);
7940         }
7941
7942         if (MovDelay[x][y])
7943         {
7944           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7945           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7946
7947           MovDelay[x][y]--;
7948           if (MovDelay[x][y])
7949             return;
7950         }
7951
7952         Feld[x][y] = EL_QUICKSAND_EMPTY;
7953         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7954         Store[x][y + 1] = Store[x][y];
7955         Store[x][y] = 0;
7956
7957         PlayLevelSoundAction(x, y, ACTION_FILLING);
7958       }
7959     }
7960     else if (element == EL_QUICKSAND_FAST_FULL)
7961     {
7962       if (IS_FREE(x, y + 1))
7963       {
7964         InitMovingField(x, y, MV_DOWN);
7965         started_moving = TRUE;
7966
7967         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7968 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7969         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7970           Store[x][y] = EL_ROCK;
7971 #else
7972         Store[x][y] = EL_ROCK;
7973 #endif
7974
7975         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7976       }
7977       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7978       {
7979         if (!MovDelay[x][y])
7980         {
7981           MovDelay[x][y] = TILEY + 1;
7982
7983           ResetGfxAnimation(x, y);
7984           ResetGfxAnimation(x, y + 1);
7985         }
7986
7987         if (MovDelay[x][y])
7988         {
7989           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7990           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7991
7992           MovDelay[x][y]--;
7993           if (MovDelay[x][y])
7994             return;
7995         }
7996
7997         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7998         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7999         Store[x][y + 1] = Store[x][y];
8000         Store[x][y] = 0;
8001
8002         PlayLevelSoundAction(x, y, ACTION_FILLING);
8003       }
8004       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8005       {
8006         if (!MovDelay[x][y])
8007         {
8008           MovDelay[x][y] = TILEY + 1;
8009
8010           ResetGfxAnimation(x, y);
8011           ResetGfxAnimation(x, y + 1);
8012         }
8013
8014         if (MovDelay[x][y])
8015         {
8016           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8017           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8018
8019           MovDelay[x][y]--;
8020           if (MovDelay[x][y])
8021             return;
8022         }
8023
8024         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8025         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8026         Store[x][y + 1] = Store[x][y];
8027         Store[x][y] = 0;
8028
8029         PlayLevelSoundAction(x, y, ACTION_FILLING);
8030       }
8031     }
8032     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8033              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8034     {
8035       InitMovingField(x, y, MV_DOWN);
8036       started_moving = TRUE;
8037
8038       Feld[x][y] = EL_QUICKSAND_FILLING;
8039       Store[x][y] = element;
8040
8041       PlayLevelSoundAction(x, y, ACTION_FILLING);
8042     }
8043     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8044              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8045     {
8046       InitMovingField(x, y, MV_DOWN);
8047       started_moving = TRUE;
8048
8049       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8050       Store[x][y] = element;
8051
8052       PlayLevelSoundAction(x, y, ACTION_FILLING);
8053     }
8054     else if (element == EL_MAGIC_WALL_FULL)
8055     {
8056       if (IS_FREE(x, y + 1))
8057       {
8058         InitMovingField(x, y, MV_DOWN);
8059         started_moving = TRUE;
8060
8061         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8062         Store[x][y] = EL_CHANGED(Store[x][y]);
8063       }
8064       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8065       {
8066         if (!MovDelay[x][y])
8067           MovDelay[x][y] = TILEY/4 + 1;
8068
8069         if (MovDelay[x][y])
8070         {
8071           MovDelay[x][y]--;
8072           if (MovDelay[x][y])
8073             return;
8074         }
8075
8076         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8077         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8078         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8079         Store[x][y] = 0;
8080       }
8081     }
8082     else if (element == EL_BD_MAGIC_WALL_FULL)
8083     {
8084       if (IS_FREE(x, y + 1))
8085       {
8086         InitMovingField(x, y, MV_DOWN);
8087         started_moving = TRUE;
8088
8089         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8090         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8091       }
8092       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8093       {
8094         if (!MovDelay[x][y])
8095           MovDelay[x][y] = TILEY/4 + 1;
8096
8097         if (MovDelay[x][y])
8098         {
8099           MovDelay[x][y]--;
8100           if (MovDelay[x][y])
8101             return;
8102         }
8103
8104         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8105         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8106         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8107         Store[x][y] = 0;
8108       }
8109     }
8110     else if (element == EL_DC_MAGIC_WALL_FULL)
8111     {
8112       if (IS_FREE(x, y + 1))
8113       {
8114         InitMovingField(x, y, MV_DOWN);
8115         started_moving = TRUE;
8116
8117         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8118         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8119       }
8120       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8121       {
8122         if (!MovDelay[x][y])
8123           MovDelay[x][y] = TILEY/4 + 1;
8124
8125         if (MovDelay[x][y])
8126         {
8127           MovDelay[x][y]--;
8128           if (MovDelay[x][y])
8129             return;
8130         }
8131
8132         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8133         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8134         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8135         Store[x][y] = 0;
8136       }
8137     }
8138     else if ((CAN_PASS_MAGIC_WALL(element) &&
8139               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8140                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8141              (CAN_PASS_DC_MAGIC_WALL(element) &&
8142               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8143
8144     {
8145       InitMovingField(x, y, MV_DOWN);
8146       started_moving = TRUE;
8147
8148       Feld[x][y] =
8149         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8150          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8151          EL_DC_MAGIC_WALL_FILLING);
8152       Store[x][y] = element;
8153     }
8154     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8155     {
8156       SplashAcid(x, y + 1);
8157
8158       InitMovingField(x, y, MV_DOWN);
8159       started_moving = TRUE;
8160
8161       Store[x][y] = EL_ACID;
8162     }
8163     else if (
8164 #if USE_FIX_IMPACT_COLLISION
8165              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8166               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8167 #else
8168              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8169               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8170 #endif
8171              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8172               CAN_FALL(element) && WasJustFalling[x][y] &&
8173               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8174
8175              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8176               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8177               (Feld[x][y + 1] == EL_BLOCKED)))
8178     {
8179       /* this is needed for a special case not covered by calling "Impact()"
8180          from "ContinueMoving()": if an element moves to a tile directly below
8181          another element which was just falling on that tile (which was empty
8182          in the previous frame), the falling element above would just stop
8183          instead of smashing the element below (in previous version, the above
8184          element was just checked for "moving" instead of "falling", resulting
8185          in incorrect smashes caused by horizontal movement of the above
8186          element; also, the case of the player being the element to smash was
8187          simply not covered here... :-/ ) */
8188
8189       CheckCollision[x][y] = 0;
8190       CheckImpact[x][y] = 0;
8191
8192       Impact(x, y);
8193     }
8194     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8195     {
8196       if (MovDir[x][y] == MV_NONE)
8197       {
8198         InitMovingField(x, y, MV_DOWN);
8199         started_moving = TRUE;
8200       }
8201     }
8202     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8203     {
8204       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8205         MovDir[x][y] = MV_DOWN;
8206
8207       InitMovingField(x, y, MV_DOWN);
8208       started_moving = TRUE;
8209     }
8210     else if (element == EL_AMOEBA_DROP)
8211     {
8212       Feld[x][y] = EL_AMOEBA_GROWING;
8213       Store[x][y] = EL_AMOEBA_WET;
8214     }
8215     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8216               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8217              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8218              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8219     {
8220       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8221                                 (IS_FREE(x - 1, y + 1) ||
8222                                  Feld[x - 1][y + 1] == EL_ACID));
8223       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8224                                 (IS_FREE(x + 1, y + 1) ||
8225                                  Feld[x + 1][y + 1] == EL_ACID));
8226       boolean can_fall_any  = (can_fall_left || can_fall_right);
8227       boolean can_fall_both = (can_fall_left && can_fall_right);
8228       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8229
8230 #if USE_NEW_ALL_SLIPPERY
8231       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8232       {
8233         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8234           can_fall_right = FALSE;
8235         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8236           can_fall_left = FALSE;
8237         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8238           can_fall_right = FALSE;
8239         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8240           can_fall_left = FALSE;
8241
8242         can_fall_any  = (can_fall_left || can_fall_right);
8243         can_fall_both = FALSE;
8244       }
8245 #else
8246       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8247       {
8248         if (slippery_type == SLIPPERY_ONLY_LEFT)
8249           can_fall_right = FALSE;
8250         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8251           can_fall_left = FALSE;
8252         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8253           can_fall_right = FALSE;
8254         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8255           can_fall_left = FALSE;
8256
8257         can_fall_any  = (can_fall_left || can_fall_right);
8258         can_fall_both = (can_fall_left && can_fall_right);
8259       }
8260 #endif
8261
8262 #if USE_NEW_ALL_SLIPPERY
8263 #else
8264 #if USE_NEW_SP_SLIPPERY
8265       /* !!! better use the same properties as for custom elements here !!! */
8266       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8267                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8268       {
8269         can_fall_right = FALSE;         /* slip down on left side */
8270         can_fall_both = FALSE;
8271       }
8272 #endif
8273 #endif
8274
8275 #if USE_NEW_ALL_SLIPPERY
8276       if (can_fall_both)
8277       {
8278         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8279           can_fall_right = FALSE;       /* slip down on left side */
8280         else
8281           can_fall_left = !(can_fall_right = RND(2));
8282
8283         can_fall_both = FALSE;
8284       }
8285 #else
8286       if (can_fall_both)
8287       {
8288         if (game.emulation == EMU_BOULDERDASH ||
8289             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8290           can_fall_right = FALSE;       /* slip down on left side */
8291         else
8292           can_fall_left = !(can_fall_right = RND(2));
8293
8294         can_fall_both = FALSE;
8295       }
8296 #endif
8297
8298       if (can_fall_any)
8299       {
8300         /* if not determined otherwise, prefer left side for slipping down */
8301         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8302         started_moving = TRUE;
8303       }
8304     }
8305 #if 0
8306     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8307 #else
8308     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8309 #endif
8310     {
8311       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8312       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8313       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8314       int belt_dir = game.belt_dir[belt_nr];
8315
8316       if ((belt_dir == MV_LEFT  && left_is_free) ||
8317           (belt_dir == MV_RIGHT && right_is_free))
8318       {
8319         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8320
8321         InitMovingField(x, y, belt_dir);
8322         started_moving = TRUE;
8323
8324         Pushed[x][y] = TRUE;
8325         Pushed[nextx][y] = TRUE;
8326
8327         GfxAction[x][y] = ACTION_DEFAULT;
8328       }
8329       else
8330       {
8331         MovDir[x][y] = 0;       /* if element was moving, stop it */
8332       }
8333     }
8334   }
8335
8336   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8337 #if 0
8338   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8339 #else
8340   if (CAN_MOVE(element) && !started_moving)
8341 #endif
8342   {
8343     int move_pattern = element_info[element].move_pattern;
8344     int newx, newy;
8345
8346 #if 0
8347 #if DEBUG
8348     if (MovDir[x][y] == MV_NONE)
8349     {
8350       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8351              x, y, element, element_info[element].token_name);
8352       printf("StartMoving(): This should never happen!\n");
8353     }
8354 #endif
8355 #endif
8356
8357     Moving2Blocked(x, y, &newx, &newy);
8358
8359     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8360       return;
8361
8362     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8363         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8364     {
8365       WasJustMoving[x][y] = 0;
8366       CheckCollision[x][y] = 0;
8367
8368       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8369
8370       if (Feld[x][y] != element)        /* element has changed */
8371         return;
8372     }
8373
8374     if (!MovDelay[x][y])        /* start new movement phase */
8375     {
8376       /* all objects that can change their move direction after each step
8377          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8378
8379       if (element != EL_YAMYAM &&
8380           element != EL_DARK_YAMYAM &&
8381           element != EL_PACMAN &&
8382           !(move_pattern & MV_ANY_DIRECTION) &&
8383           move_pattern != MV_TURNING_LEFT &&
8384           move_pattern != MV_TURNING_RIGHT &&
8385           move_pattern != MV_TURNING_LEFT_RIGHT &&
8386           move_pattern != MV_TURNING_RIGHT_LEFT &&
8387           move_pattern != MV_TURNING_RANDOM)
8388       {
8389         TurnRound(x, y);
8390
8391         if (MovDelay[x][y] && (element == EL_BUG ||
8392                                element == EL_SPACESHIP ||
8393                                element == EL_SP_SNIKSNAK ||
8394                                element == EL_SP_ELECTRON ||
8395                                element == EL_MOLE))
8396           TEST_DrawLevelField(x, y);
8397       }
8398     }
8399
8400     if (MovDelay[x][y])         /* wait some time before next movement */
8401     {
8402       MovDelay[x][y]--;
8403
8404       if (element == EL_ROBOT ||
8405           element == EL_YAMYAM ||
8406           element == EL_DARK_YAMYAM)
8407       {
8408         DrawLevelElementAnimationIfNeeded(x, y, element);
8409         PlayLevelSoundAction(x, y, ACTION_WAITING);
8410       }
8411       else if (element == EL_SP_ELECTRON)
8412         DrawLevelElementAnimationIfNeeded(x, y, element);
8413       else if (element == EL_DRAGON)
8414       {
8415         int i;
8416         int dir = MovDir[x][y];
8417         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8418         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8419         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8420                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8421                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8422                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8423         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8424
8425         GfxAction[x][y] = ACTION_ATTACKING;
8426
8427         if (IS_PLAYER(x, y))
8428           DrawPlayerField(x, y);
8429         else
8430           TEST_DrawLevelField(x, y);
8431
8432         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8433
8434         for (i = 1; i <= 3; i++)
8435         {
8436           int xx = x + i * dx;
8437           int yy = y + i * dy;
8438           int sx = SCREENX(xx);
8439           int sy = SCREENY(yy);
8440           int flame_graphic = graphic + (i - 1);
8441
8442           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8443             break;
8444
8445           if (MovDelay[x][y])
8446           {
8447             int flamed = MovingOrBlocked2Element(xx, yy);
8448
8449             /* !!! */
8450 #if 0
8451             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8452               Bang(xx, yy);
8453             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8454               RemoveMovingField(xx, yy);
8455             else
8456               RemoveField(xx, yy);
8457 #else
8458             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8459               Bang(xx, yy);
8460             else
8461               RemoveMovingField(xx, yy);
8462 #endif
8463
8464             ChangeDelay[xx][yy] = 0;
8465
8466             Feld[xx][yy] = EL_FLAMES;
8467
8468             if (IN_SCR_FIELD(sx, sy))
8469             {
8470               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8471               DrawGraphic(sx, sy, flame_graphic, frame);
8472             }
8473           }
8474           else
8475           {
8476             if (Feld[xx][yy] == EL_FLAMES)
8477               Feld[xx][yy] = EL_EMPTY;
8478             TEST_DrawLevelField(xx, yy);
8479           }
8480         }
8481       }
8482
8483       if (MovDelay[x][y])       /* element still has to wait some time */
8484       {
8485         PlayLevelSoundAction(x, y, ACTION_WAITING);
8486
8487         return;
8488       }
8489     }
8490
8491     /* now make next step */
8492
8493     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8494
8495     if (DONT_COLLIDE_WITH(element) &&
8496         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8497         !PLAYER_ENEMY_PROTECTED(newx, newy))
8498     {
8499       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8500
8501       return;
8502     }
8503
8504     else if (CAN_MOVE_INTO_ACID(element) &&
8505              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8506              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8507              (MovDir[x][y] == MV_DOWN ||
8508               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8509     {
8510       SplashAcid(newx, newy);
8511       Store[x][y] = EL_ACID;
8512     }
8513     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8514     {
8515       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8516           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8517           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8518           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8519       {
8520         RemoveField(x, y);
8521         TEST_DrawLevelField(x, y);
8522
8523         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8524         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8525           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8526
8527         local_player->friends_still_needed--;
8528         if (!local_player->friends_still_needed &&
8529             !local_player->GameOver && AllPlayersGone)
8530           PlayerWins(local_player);
8531
8532         return;
8533       }
8534       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8535       {
8536         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8537           TEST_DrawLevelField(newx, newy);
8538         else
8539           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8540       }
8541       else if (!IS_FREE(newx, newy))
8542       {
8543         GfxAction[x][y] = ACTION_WAITING;
8544
8545         if (IS_PLAYER(x, y))
8546           DrawPlayerField(x, y);
8547         else
8548           TEST_DrawLevelField(x, y);
8549
8550         return;
8551       }
8552     }
8553     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8554     {
8555       if (IS_FOOD_PIG(Feld[newx][newy]))
8556       {
8557         if (IS_MOVING(newx, newy))
8558           RemoveMovingField(newx, newy);
8559         else
8560         {
8561           Feld[newx][newy] = EL_EMPTY;
8562           TEST_DrawLevelField(newx, newy);
8563         }
8564
8565         PlayLevelSound(x, y, SND_PIG_DIGGING);
8566       }
8567       else if (!IS_FREE(newx, newy))
8568       {
8569         if (IS_PLAYER(x, y))
8570           DrawPlayerField(x, y);
8571         else
8572           TEST_DrawLevelField(x, y);
8573
8574         return;
8575       }
8576     }
8577     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8578     {
8579       if (Store[x][y] != EL_EMPTY)
8580       {
8581         boolean can_clone = FALSE;
8582         int xx, yy;
8583
8584         /* check if element to clone is still there */
8585         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8586         {
8587           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8588           {
8589             can_clone = TRUE;
8590
8591             break;
8592           }
8593         }
8594
8595         /* cannot clone or target field not free anymore -- do not clone */
8596         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8597           Store[x][y] = EL_EMPTY;
8598       }
8599
8600       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8601       {
8602         if (IS_MV_DIAGONAL(MovDir[x][y]))
8603         {
8604           int diagonal_move_dir = MovDir[x][y];
8605           int stored = Store[x][y];
8606           int change_delay = 8;
8607           int graphic;
8608
8609           /* android is moving diagonally */
8610
8611           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8612
8613           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8614           GfxElement[x][y] = EL_EMC_ANDROID;
8615           GfxAction[x][y] = ACTION_SHRINKING;
8616           GfxDir[x][y] = diagonal_move_dir;
8617           ChangeDelay[x][y] = change_delay;
8618
8619           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8620                                    GfxDir[x][y]);
8621
8622           DrawLevelGraphicAnimation(x, y, graphic);
8623           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8624
8625           if (Feld[newx][newy] == EL_ACID)
8626           {
8627             SplashAcid(newx, newy);
8628
8629             return;
8630           }
8631
8632           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8633
8634           Store[newx][newy] = EL_EMC_ANDROID;
8635           GfxElement[newx][newy] = EL_EMC_ANDROID;
8636           GfxAction[newx][newy] = ACTION_GROWING;
8637           GfxDir[newx][newy] = diagonal_move_dir;
8638           ChangeDelay[newx][newy] = change_delay;
8639
8640           graphic = el_act_dir2img(GfxElement[newx][newy],
8641                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8642
8643           DrawLevelGraphicAnimation(newx, newy, graphic);
8644           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8645
8646           return;
8647         }
8648         else
8649         {
8650           Feld[newx][newy] = EL_EMPTY;
8651           TEST_DrawLevelField(newx, newy);
8652
8653           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8654         }
8655       }
8656       else if (!IS_FREE(newx, newy))
8657       {
8658 #if 0
8659         if (IS_PLAYER(x, y))
8660           DrawPlayerField(x, y);
8661         else
8662           TEST_DrawLevelField(x, y);
8663 #endif
8664
8665         return;
8666       }
8667     }
8668     else if (IS_CUSTOM_ELEMENT(element) &&
8669              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8670     {
8671 #if 1
8672       if (!DigFieldByCE(newx, newy, element))
8673         return;
8674 #else
8675       int new_element = Feld[newx][newy];
8676
8677       if (!IS_FREE(newx, newy))
8678       {
8679         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8680                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8681                       ACTION_BREAKING);
8682
8683         /* no element can dig solid indestructible elements */
8684         if (IS_INDESTRUCTIBLE(new_element) &&
8685             !IS_DIGGABLE(new_element) &&
8686             !IS_COLLECTIBLE(new_element))
8687           return;
8688
8689         if (AmoebaNr[newx][newy] &&
8690             (new_element == EL_AMOEBA_FULL ||
8691              new_element == EL_BD_AMOEBA ||
8692              new_element == EL_AMOEBA_GROWING))
8693         {
8694           AmoebaCnt[AmoebaNr[newx][newy]]--;
8695           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8696         }
8697
8698         if (IS_MOVING(newx, newy))
8699           RemoveMovingField(newx, newy);
8700         else
8701         {
8702           RemoveField(newx, newy);
8703           TEST_DrawLevelField(newx, newy);
8704         }
8705
8706         /* if digged element was about to explode, prevent the explosion */
8707         ExplodeField[newx][newy] = EX_TYPE_NONE;
8708
8709         PlayLevelSoundAction(x, y, action);
8710       }
8711
8712       Store[newx][newy] = EL_EMPTY;
8713
8714 #if 1
8715       /* this makes it possible to leave the removed element again */
8716       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8717         Store[newx][newy] = new_element;
8718 #else
8719       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8720       {
8721         int move_leave_element = element_info[element].move_leave_element;
8722
8723         /* this makes it possible to leave the removed element again */
8724         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8725                              new_element : move_leave_element);
8726       }
8727 #endif
8728
8729 #endif
8730
8731       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8732       {
8733         RunnerVisit[x][y] = FrameCounter;
8734         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8735       }
8736     }
8737     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8738     {
8739       if (!IS_FREE(newx, newy))
8740       {
8741         if (IS_PLAYER(x, y))
8742           DrawPlayerField(x, y);
8743         else
8744           TEST_DrawLevelField(x, y);
8745
8746         return;
8747       }
8748       else
8749       {
8750         boolean wanna_flame = !RND(10);
8751         int dx = newx - x, dy = newy - y;
8752         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8753         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8754         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8755                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8756         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8757                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8758
8759         if ((wanna_flame ||
8760              IS_CLASSIC_ENEMY(element1) ||
8761              IS_CLASSIC_ENEMY(element2)) &&
8762             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8763             element1 != EL_FLAMES && element2 != EL_FLAMES)
8764         {
8765           ResetGfxAnimation(x, y);
8766           GfxAction[x][y] = ACTION_ATTACKING;
8767
8768           if (IS_PLAYER(x, y))
8769             DrawPlayerField(x, y);
8770           else
8771             TEST_DrawLevelField(x, y);
8772
8773           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8774
8775           MovDelay[x][y] = 50;
8776
8777           /* !!! */
8778 #if 0
8779           RemoveField(newx, newy);
8780 #endif
8781           Feld[newx][newy] = EL_FLAMES;
8782           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8783           {
8784 #if 0
8785             RemoveField(newx1, newy1);
8786 #endif
8787             Feld[newx1][newy1] = EL_FLAMES;
8788           }
8789           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8790           {
8791 #if 0
8792             RemoveField(newx2, newy2);
8793 #endif
8794             Feld[newx2][newy2] = EL_FLAMES;
8795           }
8796
8797           return;
8798         }
8799       }
8800     }
8801     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8802              Feld[newx][newy] == EL_DIAMOND)
8803     {
8804       if (IS_MOVING(newx, newy))
8805         RemoveMovingField(newx, newy);
8806       else
8807       {
8808         Feld[newx][newy] = EL_EMPTY;
8809         TEST_DrawLevelField(newx, newy);
8810       }
8811
8812       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8813     }
8814     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8815              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8816     {
8817       if (AmoebaNr[newx][newy])
8818       {
8819         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8820         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8821             Feld[newx][newy] == EL_BD_AMOEBA)
8822           AmoebaCnt[AmoebaNr[newx][newy]]--;
8823       }
8824
8825 #if 0
8826       /* !!! test !!! */
8827       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8828       {
8829         RemoveMovingField(newx, newy);
8830       }
8831 #else
8832       if (IS_MOVING(newx, newy))
8833       {
8834         RemoveMovingField(newx, newy);
8835       }
8836 #endif
8837       else
8838       {
8839         Feld[newx][newy] = EL_EMPTY;
8840         TEST_DrawLevelField(newx, newy);
8841       }
8842
8843       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8844     }
8845     else if ((element == EL_PACMAN || element == EL_MOLE)
8846              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8847     {
8848       if (AmoebaNr[newx][newy])
8849       {
8850         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8851         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8852             Feld[newx][newy] == EL_BD_AMOEBA)
8853           AmoebaCnt[AmoebaNr[newx][newy]]--;
8854       }
8855
8856       if (element == EL_MOLE)
8857       {
8858         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8859         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8860
8861         ResetGfxAnimation(x, y);
8862         GfxAction[x][y] = ACTION_DIGGING;
8863         TEST_DrawLevelField(x, y);
8864
8865         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8866
8867         return;                         /* wait for shrinking amoeba */
8868       }
8869       else      /* element == EL_PACMAN */
8870       {
8871         Feld[newx][newy] = EL_EMPTY;
8872         TEST_DrawLevelField(newx, newy);
8873         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8874       }
8875     }
8876     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8877              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8878               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8879     {
8880       /* wait for shrinking amoeba to completely disappear */
8881       return;
8882     }
8883     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8884     {
8885       /* object was running against a wall */
8886
8887       TurnRound(x, y);
8888
8889 #if 0
8890       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8891       if (move_pattern & MV_ANY_DIRECTION &&
8892           move_pattern == MovDir[x][y])
8893       {
8894         int blocking_element =
8895           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8896
8897         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8898                                  MovDir[x][y]);
8899
8900         element = Feld[x][y];   /* element might have changed */
8901       }
8902 #endif
8903
8904       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8905         DrawLevelElementAnimation(x, y, element);
8906
8907       if (DONT_TOUCH(element))
8908         TestIfBadThingTouchesPlayer(x, y);
8909
8910       return;
8911     }
8912
8913     InitMovingField(x, y, MovDir[x][y]);
8914
8915     PlayLevelSoundAction(x, y, ACTION_MOVING);
8916   }
8917
8918   if (MovDir[x][y])
8919     ContinueMoving(x, y);
8920 }
8921
8922 void ContinueMoving(int x, int y)
8923 {
8924   int element = Feld[x][y];
8925   struct ElementInfo *ei = &element_info[element];
8926   int direction = MovDir[x][y];
8927   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8928   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8929   int newx = x + dx, newy = y + dy;
8930   int stored = Store[x][y];
8931   int stored_new = Store[newx][newy];
8932   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8933   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8934   boolean last_line = (newy == lev_fieldy - 1);
8935
8936   MovPos[x][y] += getElementMoveStepsize(x, y);
8937
8938   if (pushed_by_player) /* special case: moving object pushed by player */
8939     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8940
8941   if (ABS(MovPos[x][y]) < TILEX)
8942   {
8943 #if 0
8944     int ee = Feld[x][y];
8945     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8946     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8947
8948     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8949            x, y, ABS(MovPos[x][y]),
8950            ee, gg, ff,
8951            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8952 #endif
8953
8954     TEST_DrawLevelField(x, y);
8955
8956     return;     /* element is still moving */
8957   }
8958
8959   /* element reached destination field */
8960
8961   Feld[x][y] = EL_EMPTY;
8962   Feld[newx][newy] = element;
8963   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8964
8965   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8966   {
8967     element = Feld[newx][newy] = EL_ACID;
8968   }
8969   else if (element == EL_MOLE)
8970   {
8971     Feld[x][y] = EL_SAND;
8972
8973     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8974   }
8975   else if (element == EL_QUICKSAND_FILLING)
8976   {
8977     element = Feld[newx][newy] = get_next_element(element);
8978     Store[newx][newy] = Store[x][y];
8979   }
8980   else if (element == EL_QUICKSAND_EMPTYING)
8981   {
8982     Feld[x][y] = get_next_element(element);
8983     element = Feld[newx][newy] = Store[x][y];
8984   }
8985   else if (element == EL_QUICKSAND_FAST_FILLING)
8986   {
8987     element = Feld[newx][newy] = get_next_element(element);
8988     Store[newx][newy] = Store[x][y];
8989   }
8990   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8991   {
8992     Feld[x][y] = get_next_element(element);
8993     element = Feld[newx][newy] = Store[x][y];
8994   }
8995   else if (element == EL_MAGIC_WALL_FILLING)
8996   {
8997     element = Feld[newx][newy] = get_next_element(element);
8998     if (!game.magic_wall_active)
8999       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9000     Store[newx][newy] = Store[x][y];
9001   }
9002   else if (element == EL_MAGIC_WALL_EMPTYING)
9003   {
9004     Feld[x][y] = get_next_element(element);
9005     if (!game.magic_wall_active)
9006       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9007     element = Feld[newx][newy] = Store[x][y];
9008
9009 #if USE_NEW_CUSTOM_VALUE
9010     InitField(newx, newy, FALSE);
9011 #endif
9012   }
9013   else if (element == EL_BD_MAGIC_WALL_FILLING)
9014   {
9015     element = Feld[newx][newy] = get_next_element(element);
9016     if (!game.magic_wall_active)
9017       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9018     Store[newx][newy] = Store[x][y];
9019   }
9020   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9021   {
9022     Feld[x][y] = get_next_element(element);
9023     if (!game.magic_wall_active)
9024       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9025     element = Feld[newx][newy] = Store[x][y];
9026
9027 #if USE_NEW_CUSTOM_VALUE
9028     InitField(newx, newy, FALSE);
9029 #endif
9030   }
9031   else if (element == EL_DC_MAGIC_WALL_FILLING)
9032   {
9033     element = Feld[newx][newy] = get_next_element(element);
9034     if (!game.magic_wall_active)
9035       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9036     Store[newx][newy] = Store[x][y];
9037   }
9038   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9039   {
9040     Feld[x][y] = get_next_element(element);
9041     if (!game.magic_wall_active)
9042       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9043     element = Feld[newx][newy] = Store[x][y];
9044
9045 #if USE_NEW_CUSTOM_VALUE
9046     InitField(newx, newy, FALSE);
9047 #endif
9048   }
9049   else if (element == EL_AMOEBA_DROPPING)
9050   {
9051     Feld[x][y] = get_next_element(element);
9052     element = Feld[newx][newy] = Store[x][y];
9053   }
9054   else if (element == EL_SOKOBAN_OBJECT)
9055   {
9056     if (Back[x][y])
9057       Feld[x][y] = Back[x][y];
9058
9059     if (Back[newx][newy])
9060       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9061
9062     Back[x][y] = Back[newx][newy] = 0;
9063   }
9064
9065   Store[x][y] = EL_EMPTY;
9066   MovPos[x][y] = 0;
9067   MovDir[x][y] = 0;
9068   MovDelay[x][y] = 0;
9069
9070   MovDelay[newx][newy] = 0;
9071
9072   if (CAN_CHANGE_OR_HAS_ACTION(element))
9073   {
9074     /* copy element change control values to new field */
9075     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9076     ChangePage[newx][newy]  = ChangePage[x][y];
9077     ChangeCount[newx][newy] = ChangeCount[x][y];
9078     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9079   }
9080
9081 #if USE_NEW_CUSTOM_VALUE
9082   CustomValue[newx][newy] = CustomValue[x][y];
9083 #endif
9084
9085   ChangeDelay[x][y] = 0;
9086   ChangePage[x][y] = -1;
9087   ChangeCount[x][y] = 0;
9088   ChangeEvent[x][y] = -1;
9089
9090 #if USE_NEW_CUSTOM_VALUE
9091   CustomValue[x][y] = 0;
9092 #endif
9093
9094   /* copy animation control values to new field */
9095   GfxFrame[newx][newy]  = GfxFrame[x][y];
9096   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9097   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9098   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9099
9100   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9101
9102   /* some elements can leave other elements behind after moving */
9103 #if 1
9104   if (ei->move_leave_element != EL_EMPTY &&
9105       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9106       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9107 #else
9108   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9109       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9110       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9111 #endif
9112   {
9113     int move_leave_element = ei->move_leave_element;
9114
9115 #if 1
9116 #if 1
9117     /* this makes it possible to leave the removed element again */
9118     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9119       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9120 #else
9121     /* this makes it possible to leave the removed element again */
9122     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9123       move_leave_element = stored;
9124 #endif
9125 #else
9126     /* this makes it possible to leave the removed element again */
9127     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9128         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9129       move_leave_element = stored;
9130 #endif
9131
9132     Feld[x][y] = move_leave_element;
9133
9134     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9135       MovDir[x][y] = direction;
9136
9137     InitField(x, y, FALSE);
9138
9139     if (GFX_CRUMBLED(Feld[x][y]))
9140       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9141
9142     if (ELEM_IS_PLAYER(move_leave_element))
9143       RelocatePlayer(x, y, move_leave_element);
9144   }
9145
9146   /* do this after checking for left-behind element */
9147   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9148
9149   if (!CAN_MOVE(element) ||
9150       (CAN_FALL(element) && direction == MV_DOWN &&
9151        (element == EL_SPRING ||
9152         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9153         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9154     GfxDir[x][y] = MovDir[newx][newy] = 0;
9155
9156   TEST_DrawLevelField(x, y);
9157   TEST_DrawLevelField(newx, newy);
9158
9159   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9160
9161   /* prevent pushed element from moving on in pushed direction */
9162   if (pushed_by_player && CAN_MOVE(element) &&
9163       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9164       !(element_info[element].move_pattern & direction))
9165     TurnRound(newx, newy);
9166
9167   /* prevent elements on conveyor belt from moving on in last direction */
9168   if (pushed_by_conveyor && CAN_FALL(element) &&
9169       direction & MV_HORIZONTAL)
9170     MovDir[newx][newy] = 0;
9171
9172   if (!pushed_by_player)
9173   {
9174     int nextx = newx + dx, nexty = newy + dy;
9175     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9176
9177     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9178
9179     if (CAN_FALL(element) && direction == MV_DOWN)
9180       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9181
9182     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9183       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9184
9185 #if USE_FIX_IMPACT_COLLISION
9186     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9187       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9188 #endif
9189   }
9190
9191   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9192   {
9193     TestIfBadThingTouchesPlayer(newx, newy);
9194     TestIfBadThingTouchesFriend(newx, newy);
9195
9196     if (!IS_CUSTOM_ELEMENT(element))
9197       TestIfBadThingTouchesOtherBadThing(newx, newy);
9198   }
9199   else if (element == EL_PENGUIN)
9200     TestIfFriendTouchesBadThing(newx, newy);
9201
9202   if (DONT_GET_HIT_BY(element))
9203   {
9204     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9205   }
9206
9207   /* give the player one last chance (one more frame) to move away */
9208   if (CAN_FALL(element) && direction == MV_DOWN &&
9209       (last_line || (!IS_FREE(x, newy + 1) &&
9210                      (!IS_PLAYER(x, newy + 1) ||
9211                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9212     Impact(x, newy);
9213
9214   if (pushed_by_player && !game.use_change_when_pushing_bug)
9215   {
9216     int push_side = MV_DIR_OPPOSITE(direction);
9217     struct PlayerInfo *player = PLAYERINFO(x, y);
9218
9219     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9220                                player->index_bit, push_side);
9221     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9222                                         player->index_bit, push_side);
9223   }
9224
9225   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9226     MovDelay[newx][newy] = 1;
9227
9228   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9229
9230   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9231
9232 #if 0
9233   if (ChangePage[newx][newy] != -1)             /* delayed change */
9234   {
9235     int page = ChangePage[newx][newy];
9236     struct ElementChangeInfo *change = &ei->change_page[page];
9237
9238     ChangePage[newx][newy] = -1;
9239
9240     if (change->can_change)
9241     {
9242       if (ChangeElement(newx, newy, element, page))
9243       {
9244         if (change->post_change_function)
9245           change->post_change_function(newx, newy);
9246       }
9247     }
9248
9249     if (change->has_action)
9250       ExecuteCustomElementAction(newx, newy, element, page);
9251   }
9252 #endif
9253
9254   TestIfElementHitsCustomElement(newx, newy, direction);
9255   TestIfPlayerTouchesCustomElement(newx, newy);
9256   TestIfElementTouchesCustomElement(newx, newy);
9257
9258   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9259       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9260     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9261                              MV_DIR_OPPOSITE(direction));
9262 }
9263
9264 int AmoebeNachbarNr(int ax, int ay)
9265 {
9266   int i;
9267   int element = Feld[ax][ay];
9268   int group_nr = 0;
9269   static int xy[4][2] =
9270   {
9271     { 0, -1 },
9272     { -1, 0 },
9273     { +1, 0 },
9274     { 0, +1 }
9275   };
9276
9277   for (i = 0; i < NUM_DIRECTIONS; i++)
9278   {
9279     int x = ax + xy[i][0];
9280     int y = ay + xy[i][1];
9281
9282     if (!IN_LEV_FIELD(x, y))
9283       continue;
9284
9285     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9286       group_nr = AmoebaNr[x][y];
9287   }
9288
9289   return group_nr;
9290 }
9291
9292 void AmoebenVereinigen(int ax, int ay)
9293 {
9294   int i, x, y, xx, yy;
9295   int new_group_nr = AmoebaNr[ax][ay];
9296   static int xy[4][2] =
9297   {
9298     { 0, -1 },
9299     { -1, 0 },
9300     { +1, 0 },
9301     { 0, +1 }
9302   };
9303
9304   if (new_group_nr == 0)
9305     return;
9306
9307   for (i = 0; i < NUM_DIRECTIONS; i++)
9308   {
9309     x = ax + xy[i][0];
9310     y = ay + xy[i][1];
9311
9312     if (!IN_LEV_FIELD(x, y))
9313       continue;
9314
9315     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9316          Feld[x][y] == EL_BD_AMOEBA ||
9317          Feld[x][y] == EL_AMOEBA_DEAD) &&
9318         AmoebaNr[x][y] != new_group_nr)
9319     {
9320       int old_group_nr = AmoebaNr[x][y];
9321
9322       if (old_group_nr == 0)
9323         return;
9324
9325       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9326       AmoebaCnt[old_group_nr] = 0;
9327       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9328       AmoebaCnt2[old_group_nr] = 0;
9329
9330       SCAN_PLAYFIELD(xx, yy)
9331       {
9332         if (AmoebaNr[xx][yy] == old_group_nr)
9333           AmoebaNr[xx][yy] = new_group_nr;
9334       }
9335     }
9336   }
9337 }
9338
9339 void AmoebeUmwandeln(int ax, int ay)
9340 {
9341   int i, x, y;
9342
9343   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9344   {
9345     int group_nr = AmoebaNr[ax][ay];
9346
9347 #ifdef DEBUG
9348     if (group_nr == 0)
9349     {
9350       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9351       printf("AmoebeUmwandeln(): This should never happen!\n");
9352       return;
9353     }
9354 #endif
9355
9356     SCAN_PLAYFIELD(x, y)
9357     {
9358       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9359       {
9360         AmoebaNr[x][y] = 0;
9361         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9362       }
9363     }
9364
9365     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9366                             SND_AMOEBA_TURNING_TO_GEM :
9367                             SND_AMOEBA_TURNING_TO_ROCK));
9368     Bang(ax, ay);
9369   }
9370   else
9371   {
9372     static int xy[4][2] =
9373     {
9374       { 0, -1 },
9375       { -1, 0 },
9376       { +1, 0 },
9377       { 0, +1 }
9378     };
9379
9380     for (i = 0; i < NUM_DIRECTIONS; i++)
9381     {
9382       x = ax + xy[i][0];
9383       y = ay + xy[i][1];
9384
9385       if (!IN_LEV_FIELD(x, y))
9386         continue;
9387
9388       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9389       {
9390         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9391                               SND_AMOEBA_TURNING_TO_GEM :
9392                               SND_AMOEBA_TURNING_TO_ROCK));
9393         Bang(x, y);
9394       }
9395     }
9396   }
9397 }
9398
9399 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9400 {
9401   int x, y;
9402   int group_nr = AmoebaNr[ax][ay];
9403   boolean done = FALSE;
9404
9405 #ifdef DEBUG
9406   if (group_nr == 0)
9407   {
9408     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9409     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9410     return;
9411   }
9412 #endif
9413
9414   SCAN_PLAYFIELD(x, y)
9415   {
9416     if (AmoebaNr[x][y] == group_nr &&
9417         (Feld[x][y] == EL_AMOEBA_DEAD ||
9418          Feld[x][y] == EL_BD_AMOEBA ||
9419          Feld[x][y] == EL_AMOEBA_GROWING))
9420     {
9421       AmoebaNr[x][y] = 0;
9422       Feld[x][y] = new_element;
9423       InitField(x, y, FALSE);
9424       TEST_DrawLevelField(x, y);
9425       done = TRUE;
9426     }
9427   }
9428
9429   if (done)
9430     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9431                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9432                             SND_BD_AMOEBA_TURNING_TO_GEM));
9433 }
9434
9435 void AmoebeWaechst(int x, int y)
9436 {
9437   static unsigned long sound_delay = 0;
9438   static unsigned long sound_delay_value = 0;
9439
9440   if (!MovDelay[x][y])          /* start new growing cycle */
9441   {
9442     MovDelay[x][y] = 7;
9443
9444     if (DelayReached(&sound_delay, sound_delay_value))
9445     {
9446       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9447       sound_delay_value = 30;
9448     }
9449   }
9450
9451   if (MovDelay[x][y])           /* wait some time before growing bigger */
9452   {
9453     MovDelay[x][y]--;
9454     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9455     {
9456       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9457                                            6 - MovDelay[x][y]);
9458
9459       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9460     }
9461
9462     if (!MovDelay[x][y])
9463     {
9464       Feld[x][y] = Store[x][y];
9465       Store[x][y] = 0;
9466       TEST_DrawLevelField(x, y);
9467     }
9468   }
9469 }
9470
9471 void AmoebaDisappearing(int x, int y)
9472 {
9473   static unsigned long sound_delay = 0;
9474   static unsigned long sound_delay_value = 0;
9475
9476   if (!MovDelay[x][y])          /* start new shrinking cycle */
9477   {
9478     MovDelay[x][y] = 7;
9479
9480     if (DelayReached(&sound_delay, sound_delay_value))
9481       sound_delay_value = 30;
9482   }
9483
9484   if (MovDelay[x][y])           /* wait some time before shrinking */
9485   {
9486     MovDelay[x][y]--;
9487     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9488     {
9489       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9490                                            6 - MovDelay[x][y]);
9491
9492       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9493     }
9494
9495     if (!MovDelay[x][y])
9496     {
9497       Feld[x][y] = EL_EMPTY;
9498       TEST_DrawLevelField(x, y);
9499
9500       /* don't let mole enter this field in this cycle;
9501          (give priority to objects falling to this field from above) */
9502       Stop[x][y] = TRUE;
9503     }
9504   }
9505 }
9506
9507 void AmoebeAbleger(int ax, int ay)
9508 {
9509   int i;
9510   int element = Feld[ax][ay];
9511   int graphic = el2img(element);
9512   int newax = ax, neway = ay;
9513   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9514   static int xy[4][2] =
9515   {
9516     { 0, -1 },
9517     { -1, 0 },
9518     { +1, 0 },
9519     { 0, +1 }
9520   };
9521
9522   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9523   {
9524     Feld[ax][ay] = EL_AMOEBA_DEAD;
9525     TEST_DrawLevelField(ax, ay);
9526     return;
9527   }
9528
9529   if (IS_ANIMATED(graphic))
9530     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9531
9532   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9533     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9534
9535   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9536   {
9537     MovDelay[ax][ay]--;
9538     if (MovDelay[ax][ay])
9539       return;
9540   }
9541
9542   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9543   {
9544     int start = RND(4);
9545     int x = ax + xy[start][0];
9546     int y = ay + xy[start][1];
9547
9548     if (!IN_LEV_FIELD(x, y))
9549       return;
9550
9551     if (IS_FREE(x, y) ||
9552         CAN_GROW_INTO(Feld[x][y]) ||
9553         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9554         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9555     {
9556       newax = x;
9557       neway = y;
9558     }
9559
9560     if (newax == ax && neway == ay)
9561       return;
9562   }
9563   else                          /* normal or "filled" (BD style) amoeba */
9564   {
9565     int start = RND(4);
9566     boolean waiting_for_player = FALSE;
9567
9568     for (i = 0; i < NUM_DIRECTIONS; i++)
9569     {
9570       int j = (start + i) % 4;
9571       int x = ax + xy[j][0];
9572       int y = ay + xy[j][1];
9573
9574       if (!IN_LEV_FIELD(x, y))
9575         continue;
9576
9577       if (IS_FREE(x, y) ||
9578           CAN_GROW_INTO(Feld[x][y]) ||
9579           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9580           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9581       {
9582         newax = x;
9583         neway = y;
9584         break;
9585       }
9586       else if (IS_PLAYER(x, y))
9587         waiting_for_player = TRUE;
9588     }
9589
9590     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9591     {
9592       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9593       {
9594         Feld[ax][ay] = EL_AMOEBA_DEAD;
9595         TEST_DrawLevelField(ax, ay);
9596         AmoebaCnt[AmoebaNr[ax][ay]]--;
9597
9598         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9599         {
9600           if (element == EL_AMOEBA_FULL)
9601             AmoebeUmwandeln(ax, ay);
9602           else if (element == EL_BD_AMOEBA)
9603             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9604         }
9605       }
9606       return;
9607     }
9608     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9609     {
9610       /* amoeba gets larger by growing in some direction */
9611
9612       int new_group_nr = AmoebaNr[ax][ay];
9613
9614 #ifdef DEBUG
9615   if (new_group_nr == 0)
9616   {
9617     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9618     printf("AmoebeAbleger(): This should never happen!\n");
9619     return;
9620   }
9621 #endif
9622
9623       AmoebaNr[newax][neway] = new_group_nr;
9624       AmoebaCnt[new_group_nr]++;
9625       AmoebaCnt2[new_group_nr]++;
9626
9627       /* if amoeba touches other amoeba(s) after growing, unify them */
9628       AmoebenVereinigen(newax, neway);
9629
9630       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9631       {
9632         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9633         return;
9634       }
9635     }
9636   }
9637
9638   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9639       (neway == lev_fieldy - 1 && newax != ax))
9640   {
9641     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9642     Store[newax][neway] = element;
9643   }
9644   else if (neway == ay || element == EL_EMC_DRIPPER)
9645   {
9646     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9647
9648     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9649   }
9650   else
9651   {
9652     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9653     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9654     Store[ax][ay] = EL_AMOEBA_DROP;
9655     ContinueMoving(ax, ay);
9656     return;
9657   }
9658
9659   TEST_DrawLevelField(newax, neway);
9660 }
9661
9662 void Life(int ax, int ay)
9663 {
9664   int x1, y1, x2, y2;
9665   int life_time = 40;
9666   int element = Feld[ax][ay];
9667   int graphic = el2img(element);
9668   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9669                          level.biomaze);
9670   boolean changed = FALSE;
9671
9672   if (IS_ANIMATED(graphic))
9673     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9674
9675   if (Stop[ax][ay])
9676     return;
9677
9678   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9679     MovDelay[ax][ay] = life_time;
9680
9681   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9682   {
9683     MovDelay[ax][ay]--;
9684     if (MovDelay[ax][ay])
9685       return;
9686   }
9687
9688   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9689   {
9690     int xx = ax+x1, yy = ay+y1;
9691     int nachbarn = 0;
9692
9693     if (!IN_LEV_FIELD(xx, yy))
9694       continue;
9695
9696     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9697     {
9698       int x = xx+x2, y = yy+y2;
9699
9700       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9701         continue;
9702
9703       if (((Feld[x][y] == element ||
9704             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9705            !Stop[x][y]) ||
9706           (IS_FREE(x, y) && Stop[x][y]))
9707         nachbarn++;
9708     }
9709
9710     if (xx == ax && yy == ay)           /* field in the middle */
9711     {
9712       if (nachbarn < life_parameter[0] ||
9713           nachbarn > life_parameter[1])
9714       {
9715         Feld[xx][yy] = EL_EMPTY;
9716         if (!Stop[xx][yy])
9717           TEST_DrawLevelField(xx, yy);
9718         Stop[xx][yy] = TRUE;
9719         changed = TRUE;
9720       }
9721     }
9722     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9723     {                                   /* free border field */
9724       if (nachbarn >= life_parameter[2] &&
9725           nachbarn <= life_parameter[3])
9726       {
9727         Feld[xx][yy] = element;
9728         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9729         if (!Stop[xx][yy])
9730           TEST_DrawLevelField(xx, yy);
9731         Stop[xx][yy] = TRUE;
9732         changed = TRUE;
9733       }
9734     }
9735   }
9736
9737   if (changed)
9738     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9739                    SND_GAME_OF_LIFE_GROWING);
9740 }
9741
9742 static void InitRobotWheel(int x, int y)
9743 {
9744   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9745 }
9746
9747 static void RunRobotWheel(int x, int y)
9748 {
9749   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9750 }
9751
9752 static void StopRobotWheel(int x, int y)
9753 {
9754   if (ZX == x && ZY == y)
9755   {
9756     ZX = ZY = -1;
9757
9758     game.robot_wheel_active = FALSE;
9759   }
9760 }
9761
9762 static void InitTimegateWheel(int x, int y)
9763 {
9764   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9765 }
9766
9767 static void RunTimegateWheel(int x, int y)
9768 {
9769   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9770 }
9771
9772 static void InitMagicBallDelay(int x, int y)
9773 {
9774 #if 1
9775   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9776 #else
9777   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9778 #endif
9779 }
9780
9781 static void ActivateMagicBall(int bx, int by)
9782 {
9783   int x, y;
9784
9785   if (level.ball_random)
9786   {
9787     int pos_border = RND(8);    /* select one of the eight border elements */
9788     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9789     int xx = pos_content % 3;
9790     int yy = pos_content / 3;
9791
9792     x = bx - 1 + xx;
9793     y = by - 1 + yy;
9794
9795     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9796       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9797   }
9798   else
9799   {
9800     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9801     {
9802       int xx = x - bx + 1;
9803       int yy = y - by + 1;
9804
9805       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9806         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9807     }
9808   }
9809
9810   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9811 }
9812
9813 void CheckExit(int x, int y)
9814 {
9815   if (local_player->gems_still_needed > 0 ||
9816       local_player->sokobanfields_still_needed > 0 ||
9817       local_player->lights_still_needed > 0)
9818   {
9819     int element = Feld[x][y];
9820     int graphic = el2img(element);
9821
9822     if (IS_ANIMATED(graphic))
9823       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9824
9825     return;
9826   }
9827
9828   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9829     return;
9830
9831   Feld[x][y] = EL_EXIT_OPENING;
9832
9833   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9834 }
9835
9836 void CheckExitEM(int x, int y)
9837 {
9838   if (local_player->gems_still_needed > 0 ||
9839       local_player->sokobanfields_still_needed > 0 ||
9840       local_player->lights_still_needed > 0)
9841   {
9842     int element = Feld[x][y];
9843     int graphic = el2img(element);
9844
9845     if (IS_ANIMATED(graphic))
9846       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9847
9848     return;
9849   }
9850
9851   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9852     return;
9853
9854   Feld[x][y] = EL_EM_EXIT_OPENING;
9855
9856   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9857 }
9858
9859 void CheckExitSteel(int x, int y)
9860 {
9861   if (local_player->gems_still_needed > 0 ||
9862       local_player->sokobanfields_still_needed > 0 ||
9863       local_player->lights_still_needed > 0)
9864   {
9865     int element = Feld[x][y];
9866     int graphic = el2img(element);
9867
9868     if (IS_ANIMATED(graphic))
9869       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9870
9871     return;
9872   }
9873
9874   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9875     return;
9876
9877   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9878
9879   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9880 }
9881
9882 void CheckExitSteelEM(int x, int y)
9883 {
9884   if (local_player->gems_still_needed > 0 ||
9885       local_player->sokobanfields_still_needed > 0 ||
9886       local_player->lights_still_needed > 0)
9887   {
9888     int element = Feld[x][y];
9889     int graphic = el2img(element);
9890
9891     if (IS_ANIMATED(graphic))
9892       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9893
9894     return;
9895   }
9896
9897   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9898     return;
9899
9900   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9901
9902   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9903 }
9904
9905 void CheckExitSP(int x, int y)
9906 {
9907   if (local_player->gems_still_needed > 0)
9908   {
9909     int element = Feld[x][y];
9910     int graphic = el2img(element);
9911
9912     if (IS_ANIMATED(graphic))
9913       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9914
9915     return;
9916   }
9917
9918   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9919     return;
9920
9921   Feld[x][y] = EL_SP_EXIT_OPENING;
9922
9923   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9924 }
9925
9926 static void CloseAllOpenTimegates()
9927 {
9928   int x, y;
9929
9930   SCAN_PLAYFIELD(x, y)
9931   {
9932     int element = Feld[x][y];
9933
9934     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9935     {
9936       Feld[x][y] = EL_TIMEGATE_CLOSING;
9937
9938       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9939     }
9940   }
9941 }
9942
9943 void DrawTwinkleOnField(int x, int y)
9944 {
9945   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9946     return;
9947
9948   if (Feld[x][y] == EL_BD_DIAMOND)
9949     return;
9950
9951   if (MovDelay[x][y] == 0)      /* next animation frame */
9952     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9953
9954   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9955   {
9956     MovDelay[x][y]--;
9957
9958     DrawLevelElementAnimation(x, y, Feld[x][y]);
9959
9960     if (MovDelay[x][y] != 0)
9961     {
9962       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9963                                            10 - MovDelay[x][y]);
9964
9965       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9966     }
9967   }
9968 }
9969
9970 void MauerWaechst(int x, int y)
9971 {
9972   int delay = 6;
9973
9974   if (!MovDelay[x][y])          /* next animation frame */
9975     MovDelay[x][y] = 3 * delay;
9976
9977   if (MovDelay[x][y])           /* wait some time before next frame */
9978   {
9979     MovDelay[x][y]--;
9980
9981     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9982     {
9983       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9984       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9985
9986       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9987     }
9988
9989     if (!MovDelay[x][y])
9990     {
9991       if (MovDir[x][y] == MV_LEFT)
9992       {
9993         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9994           TEST_DrawLevelField(x - 1, y);
9995       }
9996       else if (MovDir[x][y] == MV_RIGHT)
9997       {
9998         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9999           TEST_DrawLevelField(x + 1, y);
10000       }
10001       else if (MovDir[x][y] == MV_UP)
10002       {
10003         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10004           TEST_DrawLevelField(x, y - 1);
10005       }
10006       else
10007       {
10008         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10009           TEST_DrawLevelField(x, y + 1);
10010       }
10011
10012       Feld[x][y] = Store[x][y];
10013       Store[x][y] = 0;
10014       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10015       TEST_DrawLevelField(x, y);
10016     }
10017   }
10018 }
10019
10020 void MauerAbleger(int ax, int ay)
10021 {
10022   int element = Feld[ax][ay];
10023   int graphic = el2img(element);
10024   boolean oben_frei = FALSE, unten_frei = FALSE;
10025   boolean links_frei = FALSE, rechts_frei = FALSE;
10026   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10027   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10028   boolean new_wall = FALSE;
10029
10030   if (IS_ANIMATED(graphic))
10031     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10032
10033   if (!MovDelay[ax][ay])        /* start building new wall */
10034     MovDelay[ax][ay] = 6;
10035
10036   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10037   {
10038     MovDelay[ax][ay]--;
10039     if (MovDelay[ax][ay])
10040       return;
10041   }
10042
10043   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10044     oben_frei = TRUE;
10045   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10046     unten_frei = TRUE;
10047   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10048     links_frei = TRUE;
10049   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10050     rechts_frei = TRUE;
10051
10052   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10053       element == EL_EXPANDABLE_WALL_ANY)
10054   {
10055     if (oben_frei)
10056     {
10057       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10058       Store[ax][ay-1] = element;
10059       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10060       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10061         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10062                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10063       new_wall = TRUE;
10064     }
10065     if (unten_frei)
10066     {
10067       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10068       Store[ax][ay+1] = element;
10069       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10070       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10071         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10072                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10073       new_wall = TRUE;
10074     }
10075   }
10076
10077   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10078       element == EL_EXPANDABLE_WALL_ANY ||
10079       element == EL_EXPANDABLE_WALL ||
10080       element == EL_BD_EXPANDABLE_WALL)
10081   {
10082     if (links_frei)
10083     {
10084       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10085       Store[ax-1][ay] = element;
10086       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10087       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10088         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10089                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10090       new_wall = TRUE;
10091     }
10092
10093     if (rechts_frei)
10094     {
10095       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10096       Store[ax+1][ay] = element;
10097       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10098       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10099         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10100                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10101       new_wall = TRUE;
10102     }
10103   }
10104
10105   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10106     TEST_DrawLevelField(ax, ay);
10107
10108   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10109     oben_massiv = TRUE;
10110   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10111     unten_massiv = TRUE;
10112   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10113     links_massiv = TRUE;
10114   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10115     rechts_massiv = TRUE;
10116
10117   if (((oben_massiv && unten_massiv) ||
10118        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10119        element == EL_EXPANDABLE_WALL) &&
10120       ((links_massiv && rechts_massiv) ||
10121        element == EL_EXPANDABLE_WALL_VERTICAL))
10122     Feld[ax][ay] = EL_WALL;
10123
10124   if (new_wall)
10125     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10126 }
10127
10128 void MauerAblegerStahl(int ax, int ay)
10129 {
10130   int element = Feld[ax][ay];
10131   int graphic = el2img(element);
10132   boolean oben_frei = FALSE, unten_frei = FALSE;
10133   boolean links_frei = FALSE, rechts_frei = FALSE;
10134   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10135   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10136   boolean new_wall = FALSE;
10137
10138   if (IS_ANIMATED(graphic))
10139     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10140
10141   if (!MovDelay[ax][ay])        /* start building new wall */
10142     MovDelay[ax][ay] = 6;
10143
10144   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10145   {
10146     MovDelay[ax][ay]--;
10147     if (MovDelay[ax][ay])
10148       return;
10149   }
10150
10151   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10152     oben_frei = TRUE;
10153   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10154     unten_frei = TRUE;
10155   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10156     links_frei = TRUE;
10157   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10158     rechts_frei = TRUE;
10159
10160   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10161       element == EL_EXPANDABLE_STEELWALL_ANY)
10162   {
10163     if (oben_frei)
10164     {
10165       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10166       Store[ax][ay-1] = element;
10167       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10168       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10169         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10170                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10171       new_wall = TRUE;
10172     }
10173     if (unten_frei)
10174     {
10175       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10176       Store[ax][ay+1] = element;
10177       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10178       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10179         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10180                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10181       new_wall = TRUE;
10182     }
10183   }
10184
10185   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10186       element == EL_EXPANDABLE_STEELWALL_ANY)
10187   {
10188     if (links_frei)
10189     {
10190       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10191       Store[ax-1][ay] = element;
10192       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10193       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10194         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10195                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10196       new_wall = TRUE;
10197     }
10198
10199     if (rechts_frei)
10200     {
10201       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10202       Store[ax+1][ay] = element;
10203       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10204       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10205         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10206                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10207       new_wall = TRUE;
10208     }
10209   }
10210
10211   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10212     oben_massiv = TRUE;
10213   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10214     unten_massiv = TRUE;
10215   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10216     links_massiv = TRUE;
10217   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10218     rechts_massiv = TRUE;
10219
10220   if (((oben_massiv && unten_massiv) ||
10221        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10222       ((links_massiv && rechts_massiv) ||
10223        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10224     Feld[ax][ay] = EL_STEELWALL;
10225
10226   if (new_wall)
10227     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10228 }
10229
10230 void CheckForDragon(int x, int y)
10231 {
10232   int i, j;
10233   boolean dragon_found = FALSE;
10234   static int xy[4][2] =
10235   {
10236     { 0, -1 },
10237     { -1, 0 },
10238     { +1, 0 },
10239     { 0, +1 }
10240   };
10241
10242   for (i = 0; i < NUM_DIRECTIONS; i++)
10243   {
10244     for (j = 0; j < 4; j++)
10245     {
10246       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10247
10248       if (IN_LEV_FIELD(xx, yy) &&
10249           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10250       {
10251         if (Feld[xx][yy] == EL_DRAGON)
10252           dragon_found = TRUE;
10253       }
10254       else
10255         break;
10256     }
10257   }
10258
10259   if (!dragon_found)
10260   {
10261     for (i = 0; i < NUM_DIRECTIONS; i++)
10262     {
10263       for (j = 0; j < 3; j++)
10264       {
10265         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10266   
10267         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10268         {
10269           Feld[xx][yy] = EL_EMPTY;
10270           TEST_DrawLevelField(xx, yy);
10271         }
10272         else
10273           break;
10274       }
10275     }
10276   }
10277 }
10278
10279 static void InitBuggyBase(int x, int y)
10280 {
10281   int element = Feld[x][y];
10282   int activating_delay = FRAMES_PER_SECOND / 4;
10283
10284   ChangeDelay[x][y] =
10285     (element == EL_SP_BUGGY_BASE ?
10286      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10287      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10288      activating_delay :
10289      element == EL_SP_BUGGY_BASE_ACTIVE ?
10290      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10291 }
10292
10293 static void WarnBuggyBase(int x, int y)
10294 {
10295   int i;
10296   static int xy[4][2] =
10297   {
10298     { 0, -1 },
10299     { -1, 0 },
10300     { +1, 0 },
10301     { 0, +1 }
10302   };
10303
10304   for (i = 0; i < NUM_DIRECTIONS; i++)
10305   {
10306     int xx = x + xy[i][0];
10307     int yy = y + xy[i][1];
10308
10309     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10310     {
10311       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10312
10313       break;
10314     }
10315   }
10316 }
10317
10318 static void InitTrap(int x, int y)
10319 {
10320   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10321 }
10322
10323 static void ActivateTrap(int x, int y)
10324 {
10325   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10326 }
10327
10328 static void ChangeActiveTrap(int x, int y)
10329 {
10330   int graphic = IMG_TRAP_ACTIVE;
10331
10332   /* if new animation frame was drawn, correct crumbled sand border */
10333   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10334     TEST_DrawLevelFieldCrumbledSand(x, y);
10335 }
10336
10337 static int getSpecialActionElement(int element, int number, int base_element)
10338 {
10339   return (element != EL_EMPTY ? element :
10340           number != -1 ? base_element + number - 1 :
10341           EL_EMPTY);
10342 }
10343
10344 static int getModifiedActionNumber(int value_old, int operator, int operand,
10345                                    int value_min, int value_max)
10346 {
10347   int value_new = (operator == CA_MODE_SET      ? operand :
10348                    operator == CA_MODE_ADD      ? value_old + operand :
10349                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10350                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10351                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10352                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10353                    value_old);
10354
10355   return (value_new < value_min ? value_min :
10356           value_new > value_max ? value_max :
10357           value_new);
10358 }
10359
10360 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10361 {
10362   struct ElementInfo *ei = &element_info[element];
10363   struct ElementChangeInfo *change = &ei->change_page[page];
10364   int target_element = change->target_element;
10365   int action_type = change->action_type;
10366   int action_mode = change->action_mode;
10367   int action_arg = change->action_arg;
10368   int action_element = change->action_element;
10369   int i;
10370
10371   if (!change->has_action)
10372     return;
10373
10374   /* ---------- determine action paramater values -------------------------- */
10375
10376   int level_time_value =
10377     (level.time > 0 ? TimeLeft :
10378      TimePlayed);
10379
10380   int action_arg_element_raw =
10381     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10382      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10383      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10384      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10385      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10386      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10387      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10388      EL_EMPTY);
10389   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10390
10391 #if 0
10392   if (action_arg_element_raw == EL_GROUP_START)
10393     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10394 #endif
10395
10396   int action_arg_direction =
10397     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10398      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10399      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10400      change->actual_trigger_side :
10401      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10402      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10403      MV_NONE);
10404
10405   int action_arg_number_min =
10406     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10407      CA_ARG_MIN);
10408
10409   int action_arg_number_max =
10410     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10411      action_type == CA_SET_LEVEL_GEMS ? 999 :
10412      action_type == CA_SET_LEVEL_TIME ? 9999 :
10413      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10414      action_type == CA_SET_CE_VALUE ? 9999 :
10415      action_type == CA_SET_CE_SCORE ? 9999 :
10416      CA_ARG_MAX);
10417
10418   int action_arg_number_reset =
10419     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10420      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10421      action_type == CA_SET_LEVEL_TIME ? level.time :
10422      action_type == CA_SET_LEVEL_SCORE ? 0 :
10423 #if USE_NEW_CUSTOM_VALUE
10424      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10425 #else
10426      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10427 #endif
10428      action_type == CA_SET_CE_SCORE ? 0 :
10429      0);
10430
10431   int action_arg_number =
10432     (action_arg <= CA_ARG_MAX ? action_arg :
10433      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10434      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10435      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10436      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10437      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10438      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10439 #if USE_NEW_CUSTOM_VALUE
10440      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10441 #else
10442      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10443 #endif
10444      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10445      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10446      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10447      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10448      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10449      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10450      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10451      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10452      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10453      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10454      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10455      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10456      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10457      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10458      -1);
10459
10460   int action_arg_number_old =
10461     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10462      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10463      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10464      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10465      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10466      0);
10467
10468   int action_arg_number_new =
10469     getModifiedActionNumber(action_arg_number_old,
10470                             action_mode, action_arg_number,
10471                             action_arg_number_min, action_arg_number_max);
10472
10473 #if 1
10474   int trigger_player_bits =
10475     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10476      change->actual_trigger_player_bits : change->trigger_player);
10477 #else
10478   int trigger_player_bits =
10479     (change->actual_trigger_player >= EL_PLAYER_1 &&
10480      change->actual_trigger_player <= EL_PLAYER_4 ?
10481      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10482      PLAYER_BITS_ANY);
10483 #endif
10484
10485   int action_arg_player_bits =
10486     (action_arg >= CA_ARG_PLAYER_1 &&
10487      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10488      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10489      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10490      PLAYER_BITS_ANY);
10491
10492   /* ---------- execute action  -------------------------------------------- */
10493
10494   switch (action_type)
10495   {
10496     case CA_NO_ACTION:
10497     {
10498       return;
10499     }
10500
10501     /* ---------- level actions  ------------------------------------------- */
10502
10503     case CA_RESTART_LEVEL:
10504     {
10505       game.restart_level = TRUE;
10506
10507       break;
10508     }
10509
10510     case CA_SHOW_ENVELOPE:
10511     {
10512       int element = getSpecialActionElement(action_arg_element,
10513                                             action_arg_number, EL_ENVELOPE_1);
10514
10515       if (IS_ENVELOPE(element))
10516         local_player->show_envelope = element;
10517
10518       break;
10519     }
10520
10521     case CA_SET_LEVEL_TIME:
10522     {
10523       if (level.time > 0)       /* only modify limited time value */
10524       {
10525         TimeLeft = action_arg_number_new;
10526
10527 #if 1
10528         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10529
10530         DisplayGameControlValues();
10531 #else
10532         DrawGameValue_Time(TimeLeft);
10533 #endif
10534
10535         if (!TimeLeft && setup.time_limit)
10536           for (i = 0; i < MAX_PLAYERS; i++)
10537             KillPlayer(&stored_player[i]);
10538       }
10539
10540       break;
10541     }
10542
10543     case CA_SET_LEVEL_SCORE:
10544     {
10545       local_player->score = action_arg_number_new;
10546
10547 #if 1
10548       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10549
10550       DisplayGameControlValues();
10551 #else
10552       DrawGameValue_Score(local_player->score);
10553 #endif
10554
10555       break;
10556     }
10557
10558     case CA_SET_LEVEL_GEMS:
10559     {
10560       local_player->gems_still_needed = action_arg_number_new;
10561
10562 #if 1
10563       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10564
10565       DisplayGameControlValues();
10566 #else
10567       DrawGameValue_Emeralds(local_player->gems_still_needed);
10568 #endif
10569
10570       break;
10571     }
10572
10573 #if !USE_PLAYER_GRAVITY
10574     case CA_SET_LEVEL_GRAVITY:
10575     {
10576       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10577                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10578                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10579                       game.gravity);
10580       break;
10581     }
10582 #endif
10583
10584     case CA_SET_LEVEL_WIND:
10585     {
10586       game.wind_direction = action_arg_direction;
10587
10588       break;
10589     }
10590
10591     case CA_SET_LEVEL_RANDOM_SEED:
10592     {
10593 #if 1
10594       /* ensure that setting a new random seed while playing is predictable */
10595       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10596 #else
10597       InitRND(action_arg_number_new);
10598 #endif
10599
10600 #if 0
10601       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10602 #endif
10603
10604 #if 0
10605       {
10606         int i;
10607
10608         printf("::: ");
10609         for (i = 0; i < 9; i++)
10610           printf("%d, ", RND(2));
10611         printf("\n");
10612       }
10613 #endif
10614
10615       break;
10616     }
10617
10618     /* ---------- player actions  ------------------------------------------ */
10619
10620     case CA_MOVE_PLAYER:
10621     {
10622       /* automatically move to the next field in specified direction */
10623       for (i = 0; i < MAX_PLAYERS; i++)
10624         if (trigger_player_bits & (1 << i))
10625           stored_player[i].programmed_action = action_arg_direction;
10626
10627       break;
10628     }
10629
10630     case CA_EXIT_PLAYER:
10631     {
10632       for (i = 0; i < MAX_PLAYERS; i++)
10633         if (action_arg_player_bits & (1 << i))
10634           PlayerWins(&stored_player[i]);
10635
10636       break;
10637     }
10638
10639     case CA_KILL_PLAYER:
10640     {
10641       for (i = 0; i < MAX_PLAYERS; i++)
10642         if (action_arg_player_bits & (1 << i))
10643           KillPlayer(&stored_player[i]);
10644
10645       break;
10646     }
10647
10648     case CA_SET_PLAYER_KEYS:
10649     {
10650       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10651       int element = getSpecialActionElement(action_arg_element,
10652                                             action_arg_number, EL_KEY_1);
10653
10654       if (IS_KEY(element))
10655       {
10656         for (i = 0; i < MAX_PLAYERS; i++)
10657         {
10658           if (trigger_player_bits & (1 << i))
10659           {
10660             stored_player[i].key[KEY_NR(element)] = key_state;
10661
10662             DrawGameDoorValues();
10663           }
10664         }
10665       }
10666
10667       break;
10668     }
10669
10670     case CA_SET_PLAYER_SPEED:
10671     {
10672 #if 0
10673       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10674 #endif
10675
10676       for (i = 0; i < MAX_PLAYERS; i++)
10677       {
10678         if (trigger_player_bits & (1 << i))
10679         {
10680           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10681
10682           if (action_arg == CA_ARG_SPEED_FASTER &&
10683               stored_player[i].cannot_move)
10684           {
10685             action_arg_number = STEPSIZE_VERY_SLOW;
10686           }
10687           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10688                    action_arg == CA_ARG_SPEED_FASTER)
10689           {
10690             action_arg_number = 2;
10691             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10692                            CA_MODE_MULTIPLY);
10693           }
10694           else if (action_arg == CA_ARG_NUMBER_RESET)
10695           {
10696             action_arg_number = level.initial_player_stepsize[i];
10697           }
10698
10699           move_stepsize =
10700             getModifiedActionNumber(move_stepsize,
10701                                     action_mode,
10702                                     action_arg_number,
10703                                     action_arg_number_min,
10704                                     action_arg_number_max);
10705
10706           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10707         }
10708       }
10709
10710       break;
10711     }
10712
10713     case CA_SET_PLAYER_SHIELD:
10714     {
10715       for (i = 0; i < MAX_PLAYERS; i++)
10716       {
10717         if (trigger_player_bits & (1 << i))
10718         {
10719           if (action_arg == CA_ARG_SHIELD_OFF)
10720           {
10721             stored_player[i].shield_normal_time_left = 0;
10722             stored_player[i].shield_deadly_time_left = 0;
10723           }
10724           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10725           {
10726             stored_player[i].shield_normal_time_left = 999999;
10727           }
10728           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10729           {
10730             stored_player[i].shield_normal_time_left = 999999;
10731             stored_player[i].shield_deadly_time_left = 999999;
10732           }
10733         }
10734       }
10735
10736       break;
10737     }
10738
10739 #if USE_PLAYER_GRAVITY
10740     case CA_SET_PLAYER_GRAVITY:
10741     {
10742       for (i = 0; i < MAX_PLAYERS; i++)
10743       {
10744         if (trigger_player_bits & (1 << i))
10745         {
10746           stored_player[i].gravity =
10747             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10748              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10749              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10750              stored_player[i].gravity);
10751         }
10752       }
10753
10754       break;
10755     }
10756 #endif
10757
10758     case CA_SET_PLAYER_ARTWORK:
10759     {
10760       for (i = 0; i < MAX_PLAYERS; i++)
10761       {
10762         if (trigger_player_bits & (1 << i))
10763         {
10764           int artwork_element = action_arg_element;
10765
10766           if (action_arg == CA_ARG_ELEMENT_RESET)
10767             artwork_element =
10768               (level.use_artwork_element[i] ? level.artwork_element[i] :
10769                stored_player[i].element_nr);
10770
10771 #if USE_GFX_RESET_PLAYER_ARTWORK
10772           if (stored_player[i].artwork_element != artwork_element)
10773             stored_player[i].Frame = 0;
10774 #endif
10775
10776           stored_player[i].artwork_element = artwork_element;
10777
10778           SetPlayerWaiting(&stored_player[i], FALSE);
10779
10780           /* set number of special actions for bored and sleeping animation */
10781           stored_player[i].num_special_action_bored =
10782             get_num_special_action(artwork_element,
10783                                    ACTION_BORING_1, ACTION_BORING_LAST);
10784           stored_player[i].num_special_action_sleeping =
10785             get_num_special_action(artwork_element,
10786                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10787         }
10788       }
10789
10790       break;
10791     }
10792
10793     case CA_SET_PLAYER_INVENTORY:
10794     {
10795       for (i = 0; i < MAX_PLAYERS; i++)
10796       {
10797         struct PlayerInfo *player = &stored_player[i];
10798         int j, k;
10799
10800         if (trigger_player_bits & (1 << i))
10801         {
10802           int inventory_element = action_arg_element;
10803
10804           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10805               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10806               action_arg == CA_ARG_ELEMENT_ACTION)
10807           {
10808             int element = inventory_element;
10809             int collect_count = element_info[element].collect_count_initial;
10810
10811             if (!IS_CUSTOM_ELEMENT(element))
10812               collect_count = 1;
10813
10814             if (collect_count == 0)
10815               player->inventory_infinite_element = element;
10816             else
10817               for (k = 0; k < collect_count; k++)
10818                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10819                   player->inventory_element[player->inventory_size++] =
10820                     element;
10821           }
10822           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10823                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10824                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10825           {
10826             if (player->inventory_infinite_element != EL_UNDEFINED &&
10827                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10828                                      action_arg_element_raw))
10829               player->inventory_infinite_element = EL_UNDEFINED;
10830
10831             for (k = 0, j = 0; j < player->inventory_size; j++)
10832             {
10833               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10834                                         action_arg_element_raw))
10835                 player->inventory_element[k++] = player->inventory_element[j];
10836             }
10837
10838             player->inventory_size = k;
10839           }
10840           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10841           {
10842             if (player->inventory_size > 0)
10843             {
10844               for (j = 0; j < player->inventory_size - 1; j++)
10845                 player->inventory_element[j] = player->inventory_element[j + 1];
10846
10847               player->inventory_size--;
10848             }
10849           }
10850           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10851           {
10852             if (player->inventory_size > 0)
10853               player->inventory_size--;
10854           }
10855           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10856           {
10857             player->inventory_infinite_element = EL_UNDEFINED;
10858             player->inventory_size = 0;
10859           }
10860           else if (action_arg == CA_ARG_INVENTORY_RESET)
10861           {
10862             player->inventory_infinite_element = EL_UNDEFINED;
10863             player->inventory_size = 0;
10864
10865             if (level.use_initial_inventory[i])
10866             {
10867               for (j = 0; j < level.initial_inventory_size[i]; j++)
10868               {
10869                 int element = level.initial_inventory_content[i][j];
10870                 int collect_count = element_info[element].collect_count_initial;
10871
10872                 if (!IS_CUSTOM_ELEMENT(element))
10873                   collect_count = 1;
10874
10875                 if (collect_count == 0)
10876                   player->inventory_infinite_element = element;
10877                 else
10878                   for (k = 0; k < collect_count; k++)
10879                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10880                       player->inventory_element[player->inventory_size++] =
10881                         element;
10882               }
10883             }
10884           }
10885         }
10886       }
10887
10888       break;
10889     }
10890
10891     /* ---------- CE actions  ---------------------------------------------- */
10892
10893     case CA_SET_CE_VALUE:
10894     {
10895 #if USE_NEW_CUSTOM_VALUE
10896       int last_ce_value = CustomValue[x][y];
10897
10898       CustomValue[x][y] = action_arg_number_new;
10899
10900       if (CustomValue[x][y] != last_ce_value)
10901       {
10902         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10903         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10904
10905         if (CustomValue[x][y] == 0)
10906         {
10907           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10908           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10909         }
10910       }
10911 #endif
10912
10913       break;
10914     }
10915
10916     case CA_SET_CE_SCORE:
10917     {
10918 #if USE_NEW_CUSTOM_VALUE
10919       int last_ce_score = ei->collect_score;
10920
10921       ei->collect_score = action_arg_number_new;
10922
10923       if (ei->collect_score != last_ce_score)
10924       {
10925         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10926         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10927
10928         if (ei->collect_score == 0)
10929         {
10930           int xx, yy;
10931
10932           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10933           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10934
10935           /*
10936             This is a very special case that seems to be a mixture between
10937             CheckElementChange() and CheckTriggeredElementChange(): while
10938             the first one only affects single elements that are triggered
10939             directly, the second one affects multiple elements in the playfield
10940             that are triggered indirectly by another element. This is a third
10941             case: Changing the CE score always affects multiple identical CEs,
10942             so every affected CE must be checked, not only the single CE for
10943             which the CE score was changed in the first place (as every instance
10944             of that CE shares the same CE score, and therefore also can change)!
10945           */
10946           SCAN_PLAYFIELD(xx, yy)
10947           {
10948             if (Feld[xx][yy] == element)
10949               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10950                                  CE_SCORE_GETS_ZERO);
10951           }
10952         }
10953       }
10954 #endif
10955
10956       break;
10957     }
10958
10959     case CA_SET_CE_ARTWORK:
10960     {
10961       int artwork_element = action_arg_element;
10962       boolean reset_frame = FALSE;
10963       int xx, yy;
10964
10965       if (action_arg == CA_ARG_ELEMENT_RESET)
10966         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10967                            element);
10968
10969       if (ei->gfx_element != artwork_element)
10970         reset_frame = TRUE;
10971
10972       ei->gfx_element = artwork_element;
10973
10974       SCAN_PLAYFIELD(xx, yy)
10975       {
10976         if (Feld[xx][yy] == element)
10977         {
10978           if (reset_frame)
10979           {
10980             ResetGfxAnimation(xx, yy);
10981             ResetRandomAnimationValue(xx, yy);
10982           }
10983
10984           TEST_DrawLevelField(xx, yy);
10985         }
10986       }
10987
10988       break;
10989     }
10990
10991     /* ---------- engine actions  ------------------------------------------ */
10992
10993     case CA_SET_ENGINE_SCAN_MODE:
10994     {
10995       InitPlayfieldScanMode(action_arg);
10996
10997       break;
10998     }
10999
11000     default:
11001       break;
11002   }
11003 }
11004
11005 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11006 {
11007   int old_element = Feld[x][y];
11008   int new_element = GetElementFromGroupElement(element);
11009   int previous_move_direction = MovDir[x][y];
11010 #if USE_NEW_CUSTOM_VALUE
11011   int last_ce_value = CustomValue[x][y];
11012 #endif
11013   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11014   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11015   boolean add_player_onto_element = (new_element_is_player &&
11016 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11017                                      /* this breaks SnakeBite when a snake is
11018                                         halfway through a door that closes */
11019                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11020                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11021 #endif
11022                                      IS_WALKABLE(old_element));
11023
11024 #if 0
11025   /* check if element under the player changes from accessible to unaccessible
11026      (needed for special case of dropping element which then changes) */
11027   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11028       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11029   {
11030     Bang(x, y);
11031
11032     return;
11033   }
11034 #endif
11035
11036   if (!add_player_onto_element)
11037   {
11038     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11039       RemoveMovingField(x, y);
11040     else
11041       RemoveField(x, y);
11042
11043     Feld[x][y] = new_element;
11044
11045 #if !USE_GFX_RESET_GFX_ANIMATION
11046     ResetGfxAnimation(x, y);
11047     ResetRandomAnimationValue(x, y);
11048 #endif
11049
11050     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11051       MovDir[x][y] = previous_move_direction;
11052
11053 #if USE_NEW_CUSTOM_VALUE
11054     if (element_info[new_element].use_last_ce_value)
11055       CustomValue[x][y] = last_ce_value;
11056 #endif
11057
11058     InitField_WithBug1(x, y, FALSE);
11059
11060     new_element = Feld[x][y];   /* element may have changed */
11061
11062 #if USE_GFX_RESET_GFX_ANIMATION
11063     ResetGfxAnimation(x, y);
11064     ResetRandomAnimationValue(x, y);
11065 #endif
11066
11067     TEST_DrawLevelField(x, y);
11068
11069     if (GFX_CRUMBLED(new_element))
11070       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11071   }
11072
11073 #if 1
11074   /* check if element under the player changes from accessible to unaccessible
11075      (needed for special case of dropping element which then changes) */
11076   /* (must be checked after creating new element for walkable group elements) */
11077 #if USE_FIX_KILLED_BY_NON_WALKABLE
11078   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11079       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11080   {
11081     Bang(x, y);
11082
11083     return;
11084   }
11085 #else
11086   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11087       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11088   {
11089     Bang(x, y);
11090
11091     return;
11092   }
11093 #endif
11094 #endif
11095
11096   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11097   if (new_element_is_player)
11098     RelocatePlayer(x, y, new_element);
11099
11100   if (is_change)
11101     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11102
11103   TestIfBadThingTouchesPlayer(x, y);
11104   TestIfPlayerTouchesCustomElement(x, y);
11105   TestIfElementTouchesCustomElement(x, y);
11106 }
11107
11108 static void CreateField(int x, int y, int element)
11109 {
11110   CreateFieldExt(x, y, element, FALSE);
11111 }
11112
11113 static void CreateElementFromChange(int x, int y, int element)
11114 {
11115   element = GET_VALID_RUNTIME_ELEMENT(element);
11116
11117 #if USE_STOP_CHANGED_ELEMENTS
11118   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11119   {
11120     int old_element = Feld[x][y];
11121
11122     /* prevent changed element from moving in same engine frame
11123        unless both old and new element can either fall or move */
11124     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11125         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11126       Stop[x][y] = TRUE;
11127   }
11128 #endif
11129
11130   CreateFieldExt(x, y, element, TRUE);
11131 }
11132
11133 static boolean ChangeElement(int x, int y, int element, int page)
11134 {
11135   struct ElementInfo *ei = &element_info[element];
11136   struct ElementChangeInfo *change = &ei->change_page[page];
11137   int ce_value = CustomValue[x][y];
11138   int ce_score = ei->collect_score;
11139   int target_element;
11140   int old_element = Feld[x][y];
11141
11142   /* always use default change event to prevent running into a loop */
11143   if (ChangeEvent[x][y] == -1)
11144     ChangeEvent[x][y] = CE_DELAY;
11145
11146   if (ChangeEvent[x][y] == CE_DELAY)
11147   {
11148     /* reset actual trigger element, trigger player and action element */
11149     change->actual_trigger_element = EL_EMPTY;
11150     change->actual_trigger_player = EL_EMPTY;
11151     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11152     change->actual_trigger_side = CH_SIDE_NONE;
11153     change->actual_trigger_ce_value = 0;
11154     change->actual_trigger_ce_score = 0;
11155   }
11156
11157   /* do not change elements more than a specified maximum number of changes */
11158   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11159     return FALSE;
11160
11161   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11162
11163   if (change->explode)
11164   {
11165     Bang(x, y);
11166
11167     return TRUE;
11168   }
11169
11170   if (change->use_target_content)
11171   {
11172     boolean complete_replace = TRUE;
11173     boolean can_replace[3][3];
11174     int xx, yy;
11175
11176     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11177     {
11178       boolean is_empty;
11179       boolean is_walkable;
11180       boolean is_diggable;
11181       boolean is_collectible;
11182       boolean is_removable;
11183       boolean is_destructible;
11184       int ex = x + xx - 1;
11185       int ey = y + yy - 1;
11186       int content_element = change->target_content.e[xx][yy];
11187       int e;
11188
11189       can_replace[xx][yy] = TRUE;
11190
11191       if (ex == x && ey == y)   /* do not check changing element itself */
11192         continue;
11193
11194       if (content_element == EL_EMPTY_SPACE)
11195       {
11196         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11197
11198         continue;
11199       }
11200
11201       if (!IN_LEV_FIELD(ex, ey))
11202       {
11203         can_replace[xx][yy] = FALSE;
11204         complete_replace = FALSE;
11205
11206         continue;
11207       }
11208
11209       e = Feld[ex][ey];
11210
11211       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11212         e = MovingOrBlocked2Element(ex, ey);
11213
11214       is_empty = (IS_FREE(ex, ey) ||
11215                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11216
11217       is_walkable     = (is_empty || IS_WALKABLE(e));
11218       is_diggable     = (is_empty || IS_DIGGABLE(e));
11219       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11220       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11221       is_removable    = (is_diggable || is_collectible);
11222
11223       can_replace[xx][yy] =
11224         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11225           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11226           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11227           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11228           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11229           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11230          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11231
11232       if (!can_replace[xx][yy])
11233         complete_replace = FALSE;
11234     }
11235
11236     if (!change->only_if_complete || complete_replace)
11237     {
11238       boolean something_has_changed = FALSE;
11239
11240       if (change->only_if_complete && change->use_random_replace &&
11241           RND(100) < change->random_percentage)
11242         return FALSE;
11243
11244       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11245       {
11246         int ex = x + xx - 1;
11247         int ey = y + yy - 1;
11248         int content_element;
11249
11250         if (can_replace[xx][yy] && (!change->use_random_replace ||
11251                                     RND(100) < change->random_percentage))
11252         {
11253           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11254             RemoveMovingField(ex, ey);
11255
11256           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11257
11258           content_element = change->target_content.e[xx][yy];
11259           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11260                                               ce_value, ce_score);
11261
11262           CreateElementFromChange(ex, ey, target_element);
11263
11264           something_has_changed = TRUE;
11265
11266           /* for symmetry reasons, freeze newly created border elements */
11267           if (ex != x || ey != y)
11268             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11269         }
11270       }
11271
11272       if (something_has_changed)
11273       {
11274         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11275         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11276       }
11277     }
11278   }
11279   else
11280   {
11281     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11282                                         ce_value, ce_score);
11283
11284     if (element == EL_DIAGONAL_GROWING ||
11285         element == EL_DIAGONAL_SHRINKING)
11286     {
11287       target_element = Store[x][y];
11288
11289       Store[x][y] = EL_EMPTY;
11290     }
11291
11292     CreateElementFromChange(x, y, target_element);
11293
11294     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11295     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11296   }
11297
11298   /* this uses direct change before indirect change */
11299   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11300
11301   return TRUE;
11302 }
11303
11304 #if USE_NEW_DELAYED_ACTION
11305
11306 static void HandleElementChange(int x, int y, int page)
11307 {
11308   int element = MovingOrBlocked2Element(x, y);
11309   struct ElementInfo *ei = &element_info[element];
11310   struct ElementChangeInfo *change = &ei->change_page[page];
11311   boolean handle_action_before_change = FALSE;
11312
11313 #ifdef DEBUG
11314   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11315       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11316   {
11317     printf("\n\n");
11318     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11319            x, y, element, element_info[element].token_name);
11320     printf("HandleElementChange(): This should never happen!\n");
11321     printf("\n\n");
11322   }
11323 #endif
11324
11325   /* this can happen with classic bombs on walkable, changing elements */
11326   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11327   {
11328 #if 0
11329     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11330       ChangeDelay[x][y] = 0;
11331 #endif
11332
11333     return;
11334   }
11335
11336   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11337   {
11338     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11339
11340     if (change->can_change)
11341     {
11342 #if 1
11343       /* !!! not clear why graphic animation should be reset at all here !!! */
11344       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11345 #if USE_GFX_RESET_WHEN_NOT_MOVING
11346       /* when a custom element is about to change (for example by change delay),
11347          do not reset graphic animation when the custom element is moving */
11348       if (!IS_MOVING(x, y))
11349 #endif
11350       {
11351         ResetGfxAnimation(x, y);
11352         ResetRandomAnimationValue(x, y);
11353       }
11354 #endif
11355
11356       if (change->pre_change_function)
11357         change->pre_change_function(x, y);
11358     }
11359   }
11360
11361   ChangeDelay[x][y]--;
11362
11363   if (ChangeDelay[x][y] != 0)           /* continue element change */
11364   {
11365     if (change->can_change)
11366     {
11367       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11368
11369       if (IS_ANIMATED(graphic))
11370         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11371
11372       if (change->change_function)
11373         change->change_function(x, y);
11374     }
11375   }
11376   else                                  /* finish element change */
11377   {
11378     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11379     {
11380       page = ChangePage[x][y];
11381       ChangePage[x][y] = -1;
11382
11383       change = &ei->change_page[page];
11384     }
11385
11386     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11387     {
11388       ChangeDelay[x][y] = 1;            /* try change after next move step */
11389       ChangePage[x][y] = page;          /* remember page to use for change */
11390
11391       return;
11392     }
11393
11394 #if 1
11395     /* special case: set new level random seed before changing element */
11396     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11397       handle_action_before_change = TRUE;
11398
11399     if (change->has_action && handle_action_before_change)
11400       ExecuteCustomElementAction(x, y, element, page);
11401 #endif
11402
11403     if (change->can_change)
11404     {
11405       if (ChangeElement(x, y, element, page))
11406       {
11407         if (change->post_change_function)
11408           change->post_change_function(x, y);
11409       }
11410     }
11411
11412     if (change->has_action && !handle_action_before_change)
11413       ExecuteCustomElementAction(x, y, element, page);
11414   }
11415 }
11416
11417 #else
11418
11419 static void HandleElementChange(int x, int y, int page)
11420 {
11421   int element = MovingOrBlocked2Element(x, y);
11422   struct ElementInfo *ei = &element_info[element];
11423   struct ElementChangeInfo *change = &ei->change_page[page];
11424
11425 #ifdef DEBUG
11426   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11427   {
11428     printf("\n\n");
11429     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11430            x, y, element, element_info[element].token_name);
11431     printf("HandleElementChange(): This should never happen!\n");
11432     printf("\n\n");
11433   }
11434 #endif
11435
11436   /* this can happen with classic bombs on walkable, changing elements */
11437   if (!CAN_CHANGE(element))
11438   {
11439 #if 0
11440     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11441       ChangeDelay[x][y] = 0;
11442 #endif
11443
11444     return;
11445   }
11446
11447   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11448   {
11449     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11450
11451     ResetGfxAnimation(x, y);
11452     ResetRandomAnimationValue(x, y);
11453
11454     if (change->pre_change_function)
11455       change->pre_change_function(x, y);
11456   }
11457
11458   ChangeDelay[x][y]--;
11459
11460   if (ChangeDelay[x][y] != 0)           /* continue element change */
11461   {
11462     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11463
11464     if (IS_ANIMATED(graphic))
11465       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11466
11467     if (change->change_function)
11468       change->change_function(x, y);
11469   }
11470   else                                  /* finish element change */
11471   {
11472     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11473     {
11474       page = ChangePage[x][y];
11475       ChangePage[x][y] = -1;
11476
11477       change = &ei->change_page[page];
11478     }
11479
11480     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11481     {
11482       ChangeDelay[x][y] = 1;            /* try change after next move step */
11483       ChangePage[x][y] = page;          /* remember page to use for change */
11484
11485       return;
11486     }
11487
11488     if (ChangeElement(x, y, element, page))
11489     {
11490       if (change->post_change_function)
11491         change->post_change_function(x, y);
11492     }
11493   }
11494 }
11495
11496 #endif
11497
11498 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11499                                               int trigger_element,
11500                                               int trigger_event,
11501                                               int trigger_player,
11502                                               int trigger_side,
11503                                               int trigger_page)
11504 {
11505   boolean change_done_any = FALSE;
11506   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11507   int i;
11508
11509   if (!(trigger_events[trigger_element][trigger_event]))
11510     return FALSE;
11511
11512 #if 0
11513   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11514          trigger_event, recursion_loop_depth, recursion_loop_detected,
11515          recursion_loop_element, EL_NAME(recursion_loop_element));
11516 #endif
11517
11518   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11519
11520   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11521   {
11522     int element = EL_CUSTOM_START + i;
11523     boolean change_done = FALSE;
11524     int p;
11525
11526     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11527         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11528       continue;
11529
11530     for (p = 0; p < element_info[element].num_change_pages; p++)
11531     {
11532       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11533
11534       if (change->can_change_or_has_action &&
11535           change->has_event[trigger_event] &&
11536           change->trigger_side & trigger_side &&
11537           change->trigger_player & trigger_player &&
11538           change->trigger_page & trigger_page_bits &&
11539           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11540       {
11541         change->actual_trigger_element = trigger_element;
11542         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11543         change->actual_trigger_player_bits = trigger_player;
11544         change->actual_trigger_side = trigger_side;
11545         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11546         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11547
11548 #if 0
11549         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11550                element, EL_NAME(element), p);
11551 #endif
11552
11553         if ((change->can_change && !change_done) || change->has_action)
11554         {
11555           int x, y;
11556
11557           SCAN_PLAYFIELD(x, y)
11558           {
11559             if (Feld[x][y] == element)
11560             {
11561               if (change->can_change && !change_done)
11562               {
11563 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11564                 /* if element already changed in this frame, not only prevent
11565                    another element change (checked in ChangeElement()), but
11566                    also prevent additional element actions for this element */
11567
11568                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11569                     !level.use_action_after_change_bug)
11570                   continue;
11571 #endif
11572
11573 #if 0
11574                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11575                        element, EL_NAME(element), p);
11576 #endif
11577
11578                 ChangeDelay[x][y] = 1;
11579                 ChangeEvent[x][y] = trigger_event;
11580
11581                 HandleElementChange(x, y, p);
11582               }
11583 #if USE_NEW_DELAYED_ACTION
11584               else if (change->has_action)
11585               {
11586 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11587                 /* if element already changed in this frame, not only prevent
11588                    another element change (checked in ChangeElement()), but
11589                    also prevent additional element actions for this element */
11590
11591                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11592                     !level.use_action_after_change_bug)
11593                   continue;
11594 #endif
11595
11596
11597 #if 0
11598                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11599                        element, EL_NAME(element), p);
11600 #endif
11601
11602                 ExecuteCustomElementAction(x, y, element, p);
11603                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11604               }
11605 #else
11606               if (change->has_action)
11607               {
11608                 ExecuteCustomElementAction(x, y, element, p);
11609                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11610               }
11611 #endif
11612             }
11613           }
11614
11615           if (change->can_change)
11616           {
11617             change_done = TRUE;
11618             change_done_any = TRUE;
11619
11620 #if 0
11621             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11622                    element, EL_NAME(element), p);
11623 #endif
11624
11625           }
11626         }
11627       }
11628     }
11629   }
11630
11631   RECURSION_LOOP_DETECTION_END();
11632
11633   return change_done_any;
11634 }
11635
11636 static boolean CheckElementChangeExt(int x, int y,
11637                                      int element,
11638                                      int trigger_element,
11639                                      int trigger_event,
11640                                      int trigger_player,
11641                                      int trigger_side)
11642 {
11643   boolean change_done = FALSE;
11644   int p;
11645
11646   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11647       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11648     return FALSE;
11649
11650   if (Feld[x][y] == EL_BLOCKED)
11651   {
11652     Blocked2Moving(x, y, &x, &y);
11653     element = Feld[x][y];
11654   }
11655
11656 #if 0
11657   /* check if element has already changed */
11658   if (Feld[x][y] != element)
11659     return FALSE;
11660 #else
11661   /* check if element has already changed or is about to change after moving */
11662   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11663        Feld[x][y] != element) ||
11664
11665       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11666        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11667         ChangePage[x][y] != -1)))
11668     return FALSE;
11669 #endif
11670
11671 #if 0
11672   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11673          trigger_event, recursion_loop_depth, recursion_loop_detected,
11674          recursion_loop_element, EL_NAME(recursion_loop_element));
11675 #endif
11676
11677   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11678
11679 #if 0
11680   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11681 #endif
11682
11683   for (p = 0; p < element_info[element].num_change_pages; p++)
11684   {
11685     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11686
11687     /* check trigger element for all events where the element that is checked
11688        for changing interacts with a directly adjacent element -- this is
11689        different to element changes that affect other elements to change on the
11690        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11691     boolean check_trigger_element =
11692       (trigger_event == CE_TOUCHING_X ||
11693        trigger_event == CE_HITTING_X ||
11694        trigger_event == CE_HIT_BY_X ||
11695 #if 1
11696        /* this one was forgotten until 3.2.3 */
11697        trigger_event == CE_DIGGING_X);
11698 #endif
11699
11700     if (change->can_change_or_has_action &&
11701         change->has_event[trigger_event] &&
11702         change->trigger_side & trigger_side &&
11703         change->trigger_player & trigger_player &&
11704         (!check_trigger_element ||
11705          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11706     {
11707       change->actual_trigger_element = trigger_element;
11708       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11709       change->actual_trigger_player_bits = trigger_player;
11710       change->actual_trigger_side = trigger_side;
11711       change->actual_trigger_ce_value = CustomValue[x][y];
11712       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11713
11714       /* special case: trigger element not at (x,y) position for some events */
11715       if (check_trigger_element)
11716       {
11717         static struct
11718         {
11719           int dx, dy;
11720         } move_xy[] =
11721           {
11722             {  0,  0 },
11723             { -1,  0 },
11724             { +1,  0 },
11725             {  0,  0 },
11726             {  0, -1 },
11727             {  0,  0 }, { 0, 0 }, { 0, 0 },
11728             {  0, +1 }
11729           };
11730
11731         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11732         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11733
11734         change->actual_trigger_ce_value = CustomValue[xx][yy];
11735         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11736       }
11737
11738       if (change->can_change && !change_done)
11739       {
11740         ChangeDelay[x][y] = 1;
11741         ChangeEvent[x][y] = trigger_event;
11742
11743         HandleElementChange(x, y, p);
11744
11745         change_done = TRUE;
11746       }
11747 #if USE_NEW_DELAYED_ACTION
11748       else if (change->has_action)
11749       {
11750         ExecuteCustomElementAction(x, y, element, p);
11751         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11752       }
11753 #else
11754       if (change->has_action)
11755       {
11756         ExecuteCustomElementAction(x, y, element, p);
11757         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11758       }
11759 #endif
11760     }
11761   }
11762
11763   RECURSION_LOOP_DETECTION_END();
11764
11765   return change_done;
11766 }
11767
11768 static void PlayPlayerSound(struct PlayerInfo *player)
11769 {
11770   int jx = player->jx, jy = player->jy;
11771   int sound_element = player->artwork_element;
11772   int last_action = player->last_action_waiting;
11773   int action = player->action_waiting;
11774
11775   if (player->is_waiting)
11776   {
11777     if (action != last_action)
11778       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11779     else
11780       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11781   }
11782   else
11783   {
11784     if (action != last_action)
11785       StopSound(element_info[sound_element].sound[last_action]);
11786
11787     if (last_action == ACTION_SLEEPING)
11788       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11789   }
11790 }
11791
11792 static void PlayAllPlayersSound()
11793 {
11794   int i;
11795
11796   for (i = 0; i < MAX_PLAYERS; i++)
11797     if (stored_player[i].active)
11798       PlayPlayerSound(&stored_player[i]);
11799 }
11800
11801 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11802 {
11803   boolean last_waiting = player->is_waiting;
11804   int move_dir = player->MovDir;
11805
11806   player->dir_waiting = move_dir;
11807   player->last_action_waiting = player->action_waiting;
11808
11809   if (is_waiting)
11810   {
11811     if (!last_waiting)          /* not waiting -> waiting */
11812     {
11813       player->is_waiting = TRUE;
11814
11815       player->frame_counter_bored =
11816         FrameCounter +
11817         game.player_boring_delay_fixed +
11818         GetSimpleRandom(game.player_boring_delay_random);
11819       player->frame_counter_sleeping =
11820         FrameCounter +
11821         game.player_sleeping_delay_fixed +
11822         GetSimpleRandom(game.player_sleeping_delay_random);
11823
11824       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11825     }
11826
11827     if (game.player_sleeping_delay_fixed +
11828         game.player_sleeping_delay_random > 0 &&
11829         player->anim_delay_counter == 0 &&
11830         player->post_delay_counter == 0 &&
11831         FrameCounter >= player->frame_counter_sleeping)
11832       player->is_sleeping = TRUE;
11833     else if (game.player_boring_delay_fixed +
11834              game.player_boring_delay_random > 0 &&
11835              FrameCounter >= player->frame_counter_bored)
11836       player->is_bored = TRUE;
11837
11838     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11839                               player->is_bored ? ACTION_BORING :
11840                               ACTION_WAITING);
11841
11842     if (player->is_sleeping && player->use_murphy)
11843     {
11844       /* special case for sleeping Murphy when leaning against non-free tile */
11845
11846       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11847           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11848            !IS_MOVING(player->jx - 1, player->jy)))
11849         move_dir = MV_LEFT;
11850       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11851                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11852                 !IS_MOVING(player->jx + 1, player->jy)))
11853         move_dir = MV_RIGHT;
11854       else
11855         player->is_sleeping = FALSE;
11856
11857       player->dir_waiting = move_dir;
11858     }
11859
11860     if (player->is_sleeping)
11861     {
11862       if (player->num_special_action_sleeping > 0)
11863       {
11864         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11865         {
11866           int last_special_action = player->special_action_sleeping;
11867           int num_special_action = player->num_special_action_sleeping;
11868           int special_action =
11869             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11870              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11871              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11872              last_special_action + 1 : ACTION_SLEEPING);
11873           int special_graphic =
11874             el_act_dir2img(player->artwork_element, special_action, move_dir);
11875
11876           player->anim_delay_counter =
11877             graphic_info[special_graphic].anim_delay_fixed +
11878             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11879           player->post_delay_counter =
11880             graphic_info[special_graphic].post_delay_fixed +
11881             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11882
11883           player->special_action_sleeping = special_action;
11884         }
11885
11886         if (player->anim_delay_counter > 0)
11887         {
11888           player->action_waiting = player->special_action_sleeping;
11889           player->anim_delay_counter--;
11890         }
11891         else if (player->post_delay_counter > 0)
11892         {
11893           player->post_delay_counter--;
11894         }
11895       }
11896     }
11897     else if (player->is_bored)
11898     {
11899       if (player->num_special_action_bored > 0)
11900       {
11901         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11902         {
11903           int special_action =
11904             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11905           int special_graphic =
11906             el_act_dir2img(player->artwork_element, special_action, move_dir);
11907
11908           player->anim_delay_counter =
11909             graphic_info[special_graphic].anim_delay_fixed +
11910             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11911           player->post_delay_counter =
11912             graphic_info[special_graphic].post_delay_fixed +
11913             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11914
11915           player->special_action_bored = special_action;
11916         }
11917
11918         if (player->anim_delay_counter > 0)
11919         {
11920           player->action_waiting = player->special_action_bored;
11921           player->anim_delay_counter--;
11922         }
11923         else if (player->post_delay_counter > 0)
11924         {
11925           player->post_delay_counter--;
11926         }
11927       }
11928     }
11929   }
11930   else if (last_waiting)        /* waiting -> not waiting */
11931   {
11932     player->is_waiting = FALSE;
11933     player->is_bored = FALSE;
11934     player->is_sleeping = FALSE;
11935
11936     player->frame_counter_bored = -1;
11937     player->frame_counter_sleeping = -1;
11938
11939     player->anim_delay_counter = 0;
11940     player->post_delay_counter = 0;
11941
11942     player->dir_waiting = player->MovDir;
11943     player->action_waiting = ACTION_DEFAULT;
11944
11945     player->special_action_bored = ACTION_DEFAULT;
11946     player->special_action_sleeping = ACTION_DEFAULT;
11947   }
11948 }
11949
11950 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11951 {
11952   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11953   int left      = player_action & JOY_LEFT;
11954   int right     = player_action & JOY_RIGHT;
11955   int up        = player_action & JOY_UP;
11956   int down      = player_action & JOY_DOWN;
11957   int button1   = player_action & JOY_BUTTON_1;
11958   int button2   = player_action & JOY_BUTTON_2;
11959   int dx        = (left ? -1 : right ? 1 : 0);
11960   int dy        = (up   ? -1 : down  ? 1 : 0);
11961
11962   if (!player->active || tape.pausing)
11963     return 0;
11964
11965   if (player_action)
11966   {
11967     if (button1)
11968       snapped = SnapField(player, dx, dy);
11969     else
11970     {
11971       if (button2)
11972         dropped = DropElement(player);
11973
11974       moved = MovePlayer(player, dx, dy);
11975     }
11976
11977     if (tape.single_step && tape.recording && !tape.pausing)
11978     {
11979       if (button1 || (dropped && !moved))
11980       {
11981         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11982         SnapField(player, 0, 0);                /* stop snapping */
11983       }
11984     }
11985
11986     SetPlayerWaiting(player, FALSE);
11987
11988     return player_action;
11989   }
11990   else
11991   {
11992     /* no actions for this player (no input at player's configured device) */
11993
11994     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11995     SnapField(player, 0, 0);
11996     CheckGravityMovementWhenNotMoving(player);
11997
11998     if (player->MovPos == 0)
11999       SetPlayerWaiting(player, TRUE);
12000
12001     if (player->MovPos == 0)    /* needed for tape.playing */
12002       player->is_moving = FALSE;
12003
12004     player->is_dropping = FALSE;
12005     player->is_dropping_pressed = FALSE;
12006     player->drop_pressed_delay = 0;
12007
12008     return 0;
12009   }
12010 }
12011
12012 static void CheckLevelTime()
12013 {
12014   int i;
12015
12016   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12017   {
12018     if (level.native_em_level->lev->home == 0)  /* all players at home */
12019     {
12020       PlayerWins(local_player);
12021
12022       AllPlayersGone = TRUE;
12023
12024       level.native_em_level->lev->home = -1;
12025     }
12026
12027     if (level.native_em_level->ply[0]->alive == 0 &&
12028         level.native_em_level->ply[1]->alive == 0 &&
12029         level.native_em_level->ply[2]->alive == 0 &&
12030         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12031       AllPlayersGone = TRUE;
12032   }
12033
12034   if (TimeFrames >= FRAMES_PER_SECOND)
12035   {
12036     TimeFrames = 0;
12037     TapeTime++;
12038
12039     for (i = 0; i < MAX_PLAYERS; i++)
12040     {
12041       struct PlayerInfo *player = &stored_player[i];
12042
12043       if (SHIELD_ON(player))
12044       {
12045         player->shield_normal_time_left--;
12046
12047         if (player->shield_deadly_time_left > 0)
12048           player->shield_deadly_time_left--;
12049       }
12050     }
12051
12052     if (!local_player->LevelSolved && !level.use_step_counter)
12053     {
12054       TimePlayed++;
12055
12056       if (TimeLeft > 0)
12057       {
12058         TimeLeft--;
12059
12060         if (TimeLeft <= 10 && setup.time_limit)
12061           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12062
12063 #if 1
12064         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12065
12066         DisplayGameControlValues();
12067 #else
12068         DrawGameValue_Time(TimeLeft);
12069 #endif
12070
12071         if (!TimeLeft && setup.time_limit)
12072         {
12073           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12074             level.native_em_level->lev->killed_out_of_time = TRUE;
12075           else
12076             for (i = 0; i < MAX_PLAYERS; i++)
12077               KillPlayer(&stored_player[i]);
12078         }
12079       }
12080 #if 1
12081       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12082       {
12083         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12084
12085         DisplayGameControlValues();
12086       }
12087 #else
12088       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12089         DrawGameValue_Time(TimePlayed);
12090 #endif
12091
12092       level.native_em_level->lev->time =
12093         (level.time == 0 ? TimePlayed : TimeLeft);
12094     }
12095
12096     if (tape.recording || tape.playing)
12097       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12098   }
12099
12100 #if 1
12101   UpdateAndDisplayGameControlValues();
12102 #else
12103   UpdateGameDoorValues();
12104   DrawGameDoorValues();
12105 #endif
12106 }
12107
12108 void AdvanceFrameAndPlayerCounters(int player_nr)
12109 {
12110   int i;
12111
12112   /* advance frame counters (global frame counter and time frame counter) */
12113   FrameCounter++;
12114   TimeFrames++;
12115
12116   /* advance player counters (counters for move delay, move animation etc.) */
12117   for (i = 0; i < MAX_PLAYERS; i++)
12118   {
12119     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12120     int move_delay_value = stored_player[i].move_delay_value;
12121     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12122
12123     if (!advance_player_counters)       /* not all players may be affected */
12124       continue;
12125
12126 #if USE_NEW_PLAYER_ANIM
12127     if (move_frames == 0)       /* less than one move per game frame */
12128     {
12129       int stepsize = TILEX / move_delay_value;
12130       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12131       int count = (stored_player[i].is_moving ?
12132                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12133
12134       if (count % delay == 0)
12135         move_frames = 1;
12136     }
12137 #endif
12138
12139     stored_player[i].Frame += move_frames;
12140
12141     if (stored_player[i].MovPos != 0)
12142       stored_player[i].StepFrame += move_frames;
12143
12144     if (stored_player[i].move_delay > 0)
12145       stored_player[i].move_delay--;
12146
12147     /* due to bugs in previous versions, counter must count up, not down */
12148     if (stored_player[i].push_delay != -1)
12149       stored_player[i].push_delay++;
12150
12151     if (stored_player[i].drop_delay > 0)
12152       stored_player[i].drop_delay--;
12153
12154     if (stored_player[i].is_dropping_pressed)
12155       stored_player[i].drop_pressed_delay++;
12156   }
12157 }
12158
12159 void StartGameActions(boolean init_network_game, boolean record_tape,
12160                       long random_seed)
12161 {
12162   unsigned long new_random_seed = InitRND(random_seed);
12163
12164   if (record_tape)
12165     TapeStartRecording(new_random_seed);
12166
12167 #if defined(NETWORK_AVALIABLE)
12168   if (init_network_game)
12169   {
12170     SendToServer_StartPlaying();
12171
12172     return;
12173   }
12174 #endif
12175
12176   InitGame();
12177 }
12178
12179 void GameActions()
12180 {
12181   static unsigned long game_frame_delay = 0;
12182   unsigned long game_frame_delay_value;
12183   byte *recorded_player_action;
12184   byte summarized_player_action = 0;
12185   byte tape_action[MAX_PLAYERS];
12186   int i;
12187
12188   /* detect endless loops, caused by custom element programming */
12189   if (recursion_loop_detected && recursion_loop_depth == 0)
12190   {
12191     char *message = getStringCat3("Internal Error ! Element ",
12192                                   EL_NAME(recursion_loop_element),
12193                                   " caused endless loop ! Quit the game ?");
12194
12195     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12196           EL_NAME(recursion_loop_element));
12197
12198     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12199
12200     recursion_loop_detected = FALSE;    /* if game should be continued */
12201
12202     free(message);
12203
12204     return;
12205   }
12206
12207   if (game.restart_level)
12208     StartGameActions(options.network, setup.autorecord, level.random_seed);
12209
12210   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12211   {
12212     if (level.native_em_level->lev->home == 0)  /* all players at home */
12213     {
12214       PlayerWins(local_player);
12215
12216       AllPlayersGone = TRUE;
12217
12218       level.native_em_level->lev->home = -1;
12219     }
12220
12221     if (level.native_em_level->ply[0]->alive == 0 &&
12222         level.native_em_level->ply[1]->alive == 0 &&
12223         level.native_em_level->ply[2]->alive == 0 &&
12224         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12225       AllPlayersGone = TRUE;
12226   }
12227
12228   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12229     GameWon();
12230
12231   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12232     TapeStop();
12233
12234   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12235     return;
12236
12237   game_frame_delay_value =
12238     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12239
12240   if (tape.playing && tape.warp_forward && !tape.pausing)
12241     game_frame_delay_value = 0;
12242
12243   /* ---------- main game synchronization point ---------- */
12244
12245   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12246
12247   if (network_playing && !network_player_action_received)
12248   {
12249     /* try to get network player actions in time */
12250
12251 #if defined(NETWORK_AVALIABLE)
12252     /* last chance to get network player actions without main loop delay */
12253     HandleNetworking();
12254 #endif
12255
12256     /* game was quit by network peer */
12257     if (game_status != GAME_MODE_PLAYING)
12258       return;
12259
12260     if (!network_player_action_received)
12261       return;           /* failed to get network player actions in time */
12262
12263     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12264   }
12265
12266   if (tape.pausing)
12267     return;
12268
12269   /* at this point we know that we really continue executing the game */
12270
12271   network_player_action_received = FALSE;
12272
12273   /* when playing tape, read previously recorded player input from tape data */
12274   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12275
12276 #if 1
12277   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12278   if (tape.pausing)
12279     return;
12280 #endif
12281
12282   if (tape.set_centered_player)
12283   {
12284     game.centered_player_nr_next = tape.centered_player_nr_next;
12285     game.set_centered_player = TRUE;
12286   }
12287
12288   for (i = 0; i < MAX_PLAYERS; i++)
12289   {
12290     summarized_player_action |= stored_player[i].action;
12291
12292     if (!network_playing)
12293       stored_player[i].effective_action = stored_player[i].action;
12294   }
12295
12296 #if defined(NETWORK_AVALIABLE)
12297   if (network_playing)
12298     SendToServer_MovePlayer(summarized_player_action);
12299 #endif
12300
12301   if (!options.network && !setup.team_mode)
12302     local_player->effective_action = summarized_player_action;
12303
12304   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12305   {
12306     for (i = 0; i < MAX_PLAYERS; i++)
12307       stored_player[i].effective_action =
12308         (i == game.centered_player_nr ? summarized_player_action : 0);
12309   }
12310
12311   if (recorded_player_action != NULL)
12312     for (i = 0; i < MAX_PLAYERS; i++)
12313       stored_player[i].effective_action = recorded_player_action[i];
12314
12315   for (i = 0; i < MAX_PLAYERS; i++)
12316   {
12317     tape_action[i] = stored_player[i].effective_action;
12318
12319     /* (this can only happen in the R'n'D game engine) */
12320     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12321       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12322   }
12323
12324   /* only record actions from input devices, but not programmed actions */
12325   if (tape.recording)
12326     TapeRecordAction(tape_action);
12327
12328 #if USE_NEW_PLAYER_ASSIGNMENTS
12329   {
12330     byte mapped_action[MAX_PLAYERS];
12331
12332     for (i = 0; i < MAX_PLAYERS; i++)
12333       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12334
12335     for (i = 0; i < MAX_PLAYERS; i++)
12336       stored_player[i].effective_action = mapped_action[i];
12337   }
12338 #endif
12339
12340   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12341   {
12342     GameActions_EM_Main();
12343   }
12344   else
12345   {
12346     GameActions_RND();
12347   }
12348 }
12349
12350 void GameActions_EM_Main()
12351 {
12352   byte effective_action[MAX_PLAYERS];
12353   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12354   int i;
12355
12356   for (i = 0; i < MAX_PLAYERS; i++)
12357     effective_action[i] = stored_player[i].effective_action;
12358
12359   GameActions_EM(effective_action, warp_mode);
12360
12361   CheckLevelTime();
12362
12363   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12364 }
12365
12366 void GameActions_RND()
12367 {
12368   int magic_wall_x = 0, magic_wall_y = 0;
12369   int i, x, y, element, graphic;
12370
12371   InitPlayfieldScanModeVars();
12372
12373 #if USE_ONE_MORE_CHANGE_PER_FRAME
12374   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12375   {
12376     SCAN_PLAYFIELD(x, y)
12377     {
12378       ChangeCount[x][y] = 0;
12379       ChangeEvent[x][y] = -1;
12380     }
12381   }
12382 #endif
12383
12384   if (game.set_centered_player)
12385   {
12386     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12387
12388     /* switching to "all players" only possible if all players fit to screen */
12389     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12390     {
12391       game.centered_player_nr_next = game.centered_player_nr;
12392       game.set_centered_player = FALSE;
12393     }
12394
12395     /* do not switch focus to non-existing (or non-active) player */
12396     if (game.centered_player_nr_next >= 0 &&
12397         !stored_player[game.centered_player_nr_next].active)
12398     {
12399       game.centered_player_nr_next = game.centered_player_nr;
12400       game.set_centered_player = FALSE;
12401     }
12402   }
12403
12404   if (game.set_centered_player &&
12405       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12406   {
12407     int sx, sy;
12408
12409     if (game.centered_player_nr_next == -1)
12410     {
12411       setScreenCenteredToAllPlayers(&sx, &sy);
12412     }
12413     else
12414     {
12415       sx = stored_player[game.centered_player_nr_next].jx;
12416       sy = stored_player[game.centered_player_nr_next].jy;
12417     }
12418
12419     game.centered_player_nr = game.centered_player_nr_next;
12420     game.set_centered_player = FALSE;
12421
12422     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12423     DrawGameDoorValues();
12424   }
12425
12426   for (i = 0; i < MAX_PLAYERS; i++)
12427   {
12428     int actual_player_action = stored_player[i].effective_action;
12429
12430 #if 1
12431     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12432        - rnd_equinox_tetrachloride 048
12433        - rnd_equinox_tetrachloride_ii 096
12434        - rnd_emanuel_schmieg 002
12435        - doctor_sloan_ww 001, 020
12436     */
12437     if (stored_player[i].MovPos == 0)
12438       CheckGravityMovement(&stored_player[i]);
12439 #endif
12440
12441     /* overwrite programmed action with tape action */
12442     if (stored_player[i].programmed_action)
12443       actual_player_action = stored_player[i].programmed_action;
12444
12445     PlayerActions(&stored_player[i], actual_player_action);
12446
12447     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12448   }
12449
12450   ScrollScreen(NULL, SCROLL_GO_ON);
12451
12452   /* for backwards compatibility, the following code emulates a fixed bug that
12453      occured when pushing elements (causing elements that just made their last
12454      pushing step to already (if possible) make their first falling step in the
12455      same game frame, which is bad); this code is also needed to use the famous
12456      "spring push bug" which is used in older levels and might be wanted to be
12457      used also in newer levels, but in this case the buggy pushing code is only
12458      affecting the "spring" element and no other elements */
12459
12460   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12461   {
12462     for (i = 0; i < MAX_PLAYERS; i++)
12463     {
12464       struct PlayerInfo *player = &stored_player[i];
12465       int x = player->jx;
12466       int y = player->jy;
12467
12468       if (player->active && player->is_pushing && player->is_moving &&
12469           IS_MOVING(x, y) &&
12470           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12471            Feld[x][y] == EL_SPRING))
12472       {
12473         ContinueMoving(x, y);
12474
12475         /* continue moving after pushing (this is actually a bug) */
12476         if (!IS_MOVING(x, y))
12477           Stop[x][y] = FALSE;
12478       }
12479     }
12480   }
12481
12482 #if 0
12483   debug_print_timestamp(0, "start main loop profiling");
12484 #endif
12485
12486   SCAN_PLAYFIELD(x, y)
12487   {
12488     ChangeCount[x][y] = 0;
12489     ChangeEvent[x][y] = -1;
12490
12491     /* this must be handled before main playfield loop */
12492     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12493     {
12494       MovDelay[x][y]--;
12495       if (MovDelay[x][y] <= 0)
12496         RemoveField(x, y);
12497     }
12498
12499 #if USE_NEW_SNAP_DELAY
12500     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12501     {
12502       MovDelay[x][y]--;
12503       if (MovDelay[x][y] <= 0)
12504       {
12505         RemoveField(x, y);
12506         TEST_DrawLevelField(x, y);
12507
12508         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12509       }
12510     }
12511 #endif
12512
12513 #if DEBUG
12514     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12515     {
12516       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12517       printf("GameActions(): This should never happen!\n");
12518
12519       ChangePage[x][y] = -1;
12520     }
12521 #endif
12522
12523     Stop[x][y] = FALSE;
12524     if (WasJustMoving[x][y] > 0)
12525       WasJustMoving[x][y]--;
12526     if (WasJustFalling[x][y] > 0)
12527       WasJustFalling[x][y]--;
12528     if (CheckCollision[x][y] > 0)
12529       CheckCollision[x][y]--;
12530     if (CheckImpact[x][y] > 0)
12531       CheckImpact[x][y]--;
12532
12533     GfxFrame[x][y]++;
12534
12535     /* reset finished pushing action (not done in ContinueMoving() to allow
12536        continuous pushing animation for elements with zero push delay) */
12537     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12538     {
12539       ResetGfxAnimation(x, y);
12540       TEST_DrawLevelField(x, y);
12541     }
12542
12543 #if DEBUG
12544     if (IS_BLOCKED(x, y))
12545     {
12546       int oldx, oldy;
12547
12548       Blocked2Moving(x, y, &oldx, &oldy);
12549       if (!IS_MOVING(oldx, oldy))
12550       {
12551         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12552         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12553         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12554         printf("GameActions(): This should never happen!\n");
12555       }
12556     }
12557 #endif
12558   }
12559
12560 #if 0
12561   debug_print_timestamp(0, "- time for pre-main loop:");
12562 #endif
12563
12564 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12565   SCAN_PLAYFIELD(x, y)
12566   {
12567     element = Feld[x][y];
12568     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12569
12570 #if 1
12571     {
12572 #if 1
12573       int element2 = element;
12574       int graphic2 = graphic;
12575 #else
12576       int element2 = Feld[x][y];
12577       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12578 #endif
12579       int last_gfx_frame = GfxFrame[x][y];
12580
12581       if (graphic_info[graphic2].anim_global_sync)
12582         GfxFrame[x][y] = FrameCounter;
12583       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12584         GfxFrame[x][y] = CustomValue[x][y];
12585       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12586         GfxFrame[x][y] = element_info[element2].collect_score;
12587       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12588         GfxFrame[x][y] = ChangeDelay[x][y];
12589
12590       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12591         DrawLevelGraphicAnimation(x, y, graphic2);
12592     }
12593 #else
12594     ResetGfxFrame(x, y, TRUE);
12595 #endif
12596
12597 #if 1
12598     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600       ResetRandomAnimationValue(x, y);
12601 #endif
12602
12603 #if 1
12604     SetRandomAnimationValue(x, y);
12605 #endif
12606
12607 #if 1
12608     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12609 #endif
12610   }
12611 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12612
12613 #if 0
12614   debug_print_timestamp(0, "- time for TEST loop:     -->");
12615 #endif
12616
12617   SCAN_PLAYFIELD(x, y)
12618   {
12619     element = Feld[x][y];
12620     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12621
12622     ResetGfxFrame(x, y, TRUE);
12623
12624     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12625         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12626       ResetRandomAnimationValue(x, y);
12627
12628     SetRandomAnimationValue(x, y);
12629
12630     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12631
12632     if (IS_INACTIVE(element))
12633     {
12634       if (IS_ANIMATED(graphic))
12635         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12636
12637       continue;
12638     }
12639
12640     /* this may take place after moving, so 'element' may have changed */
12641     if (IS_CHANGING(x, y) &&
12642         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12643     {
12644       int page = element_info[element].event_page_nr[CE_DELAY];
12645
12646 #if 1
12647       HandleElementChange(x, y, page);
12648 #else
12649       if (CAN_CHANGE(element))
12650         HandleElementChange(x, y, page);
12651
12652       if (HAS_ACTION(element))
12653         ExecuteCustomElementAction(x, y, element, page);
12654 #endif
12655
12656       element = Feld[x][y];
12657       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12658     }
12659
12660 #if 0   // ---------------------------------------------------------------------
12661
12662     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12663     {
12664       StartMoving(x, y);
12665
12666       element = Feld[x][y];
12667       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12668
12669       if (IS_ANIMATED(graphic) &&
12670           !IS_MOVING(x, y) &&
12671           !Stop[x][y])
12672         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12673
12674       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12675         TEST_DrawTwinkleOnField(x, y);
12676     }
12677     else if (IS_MOVING(x, y))
12678       ContinueMoving(x, y);
12679     else
12680     {
12681       switch (element)
12682       {
12683         case EL_ACID:
12684         case EL_EXIT_OPEN:
12685         case EL_EM_EXIT_OPEN:
12686         case EL_SP_EXIT_OPEN:
12687         case EL_STEEL_EXIT_OPEN:
12688         case EL_EM_STEEL_EXIT_OPEN:
12689         case EL_SP_TERMINAL:
12690         case EL_SP_TERMINAL_ACTIVE:
12691         case EL_EXTRA_TIME:
12692         case EL_SHIELD_NORMAL:
12693         case EL_SHIELD_DEADLY:
12694           if (IS_ANIMATED(graphic))
12695             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12696           break;
12697
12698         case EL_DYNAMITE_ACTIVE:
12699         case EL_EM_DYNAMITE_ACTIVE:
12700         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12701         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12702         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12703         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12704         case EL_SP_DISK_RED_ACTIVE:
12705           CheckDynamite(x, y);
12706           break;
12707
12708         case EL_AMOEBA_GROWING:
12709           AmoebeWaechst(x, y);
12710           break;
12711
12712         case EL_AMOEBA_SHRINKING:
12713           AmoebaDisappearing(x, y);
12714           break;
12715
12716 #if !USE_NEW_AMOEBA_CODE
12717         case EL_AMOEBA_WET:
12718         case EL_AMOEBA_DRY:
12719         case EL_AMOEBA_FULL:
12720         case EL_BD_AMOEBA:
12721         case EL_EMC_DRIPPER:
12722           AmoebeAbleger(x, y);
12723           break;
12724 #endif
12725
12726         case EL_GAME_OF_LIFE:
12727         case EL_BIOMAZE:
12728           Life(x, y);
12729           break;
12730
12731         case EL_EXIT_CLOSED:
12732           CheckExit(x, y);
12733           break;
12734
12735         case EL_EM_EXIT_CLOSED:
12736           CheckExitEM(x, y);
12737           break;
12738
12739         case EL_STEEL_EXIT_CLOSED:
12740           CheckExitSteel(x, y);
12741           break;
12742
12743         case EL_EM_STEEL_EXIT_CLOSED:
12744           CheckExitSteelEM(x, y);
12745           break;
12746
12747         case EL_SP_EXIT_CLOSED:
12748           CheckExitSP(x, y);
12749           break;
12750
12751         case EL_EXPANDABLE_WALL_GROWING:
12752         case EL_EXPANDABLE_STEELWALL_GROWING:
12753           MauerWaechst(x, y);
12754           break;
12755
12756         case EL_EXPANDABLE_WALL:
12757         case EL_EXPANDABLE_WALL_HORIZONTAL:
12758         case EL_EXPANDABLE_WALL_VERTICAL:
12759         case EL_EXPANDABLE_WALL_ANY:
12760         case EL_BD_EXPANDABLE_WALL:
12761           MauerAbleger(x, y);
12762           break;
12763
12764         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12765         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12766         case EL_EXPANDABLE_STEELWALL_ANY:
12767           MauerAblegerStahl(x, y);
12768           break;
12769
12770         case EL_FLAMES:
12771           CheckForDragon(x, y);
12772           break;
12773
12774         case EL_EXPLOSION:
12775           break;
12776
12777         case EL_ELEMENT_SNAPPING:
12778         case EL_DIAGONAL_SHRINKING:
12779         case EL_DIAGONAL_GROWING:
12780         {
12781           graphic =
12782             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12783
12784           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12785           break;
12786         }
12787
12788         default:
12789           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12790             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12791           break;
12792       }
12793     }
12794
12795 #else   // ---------------------------------------------------------------------
12796
12797     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12798     {
12799       StartMoving(x, y);
12800
12801       element = Feld[x][y];
12802       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12803
12804       if (IS_ANIMATED(graphic) &&
12805           !IS_MOVING(x, y) &&
12806           !Stop[x][y])
12807         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12808
12809       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12810         TEST_DrawTwinkleOnField(x, y);
12811     }
12812     else if ((element == EL_ACID ||
12813               element == EL_EXIT_OPEN ||
12814               element == EL_EM_EXIT_OPEN ||
12815               element == EL_SP_EXIT_OPEN ||
12816               element == EL_STEEL_EXIT_OPEN ||
12817               element == EL_EM_STEEL_EXIT_OPEN ||
12818               element == EL_SP_TERMINAL ||
12819               element == EL_SP_TERMINAL_ACTIVE ||
12820               element == EL_EXTRA_TIME ||
12821               element == EL_SHIELD_NORMAL ||
12822               element == EL_SHIELD_DEADLY) &&
12823              IS_ANIMATED(graphic))
12824       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12825     else if (IS_MOVING(x, y))
12826       ContinueMoving(x, y);
12827     else if (IS_ACTIVE_BOMB(element))
12828       CheckDynamite(x, y);
12829     else if (element == EL_AMOEBA_GROWING)
12830       AmoebeWaechst(x, y);
12831     else if (element == EL_AMOEBA_SHRINKING)
12832       AmoebaDisappearing(x, y);
12833
12834 #if !USE_NEW_AMOEBA_CODE
12835     else if (IS_AMOEBALIVE(element))
12836       AmoebeAbleger(x, y);
12837 #endif
12838
12839     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12840       Life(x, y);
12841     else if (element == EL_EXIT_CLOSED)
12842       CheckExit(x, y);
12843     else if (element == EL_EM_EXIT_CLOSED)
12844       CheckExitEM(x, y);
12845     else if (element == EL_STEEL_EXIT_CLOSED)
12846       CheckExitSteel(x, y);
12847     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12848       CheckExitSteelEM(x, y);
12849     else if (element == EL_SP_EXIT_CLOSED)
12850       CheckExitSP(x, y);
12851     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12852              element == EL_EXPANDABLE_STEELWALL_GROWING)
12853       MauerWaechst(x, y);
12854     else if (element == EL_EXPANDABLE_WALL ||
12855              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12856              element == EL_EXPANDABLE_WALL_VERTICAL ||
12857              element == EL_EXPANDABLE_WALL_ANY ||
12858              element == EL_BD_EXPANDABLE_WALL)
12859       MauerAbleger(x, y);
12860     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12861              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12862              element == EL_EXPANDABLE_STEELWALL_ANY)
12863       MauerAblegerStahl(x, y);
12864     else if (element == EL_FLAMES)
12865       CheckForDragon(x, y);
12866     else if (element == EL_EXPLOSION)
12867       ; /* drawing of correct explosion animation is handled separately */
12868     else if (element == EL_ELEMENT_SNAPPING ||
12869              element == EL_DIAGONAL_SHRINKING ||
12870              element == EL_DIAGONAL_GROWING)
12871     {
12872       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12873
12874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12875     }
12876     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12877       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12878
12879 #endif  // ---------------------------------------------------------------------
12880
12881     if (IS_BELT_ACTIVE(element))
12882       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12883
12884     if (game.magic_wall_active)
12885     {
12886       int jx = local_player->jx, jy = local_player->jy;
12887
12888       /* play the element sound at the position nearest to the player */
12889       if ((element == EL_MAGIC_WALL_FULL ||
12890            element == EL_MAGIC_WALL_ACTIVE ||
12891            element == EL_MAGIC_WALL_EMPTYING ||
12892            element == EL_BD_MAGIC_WALL_FULL ||
12893            element == EL_BD_MAGIC_WALL_ACTIVE ||
12894            element == EL_BD_MAGIC_WALL_EMPTYING ||
12895            element == EL_DC_MAGIC_WALL_FULL ||
12896            element == EL_DC_MAGIC_WALL_ACTIVE ||
12897            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12898           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12899       {
12900         magic_wall_x = x;
12901         magic_wall_y = y;
12902       }
12903     }
12904   }
12905
12906 #if 0
12907   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12908 #endif
12909
12910 #if USE_NEW_AMOEBA_CODE
12911   /* new experimental amoeba growth stuff */
12912   if (!(FrameCounter % 8))
12913   {
12914     static unsigned long random = 1684108901;
12915
12916     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12917     {
12918       x = RND(lev_fieldx);
12919       y = RND(lev_fieldy);
12920       element = Feld[x][y];
12921
12922       if (!IS_PLAYER(x,y) &&
12923           (element == EL_EMPTY ||
12924            CAN_GROW_INTO(element) ||
12925            element == EL_QUICKSAND_EMPTY ||
12926            element == EL_QUICKSAND_FAST_EMPTY ||
12927            element == EL_ACID_SPLASH_LEFT ||
12928            element == EL_ACID_SPLASH_RIGHT))
12929       {
12930         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12931             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12932             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12933             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12934           Feld[x][y] = EL_AMOEBA_DROP;
12935       }
12936
12937       random = random * 129 + 1;
12938     }
12939   }
12940 #endif
12941
12942 #if 0
12943   if (game.explosions_delayed)
12944 #endif
12945   {
12946     game.explosions_delayed = FALSE;
12947
12948     SCAN_PLAYFIELD(x, y)
12949     {
12950       element = Feld[x][y];
12951
12952       if (ExplodeField[x][y])
12953         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12954       else if (element == EL_EXPLOSION)
12955         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12956
12957       ExplodeField[x][y] = EX_TYPE_NONE;
12958     }
12959
12960     game.explosions_delayed = TRUE;
12961   }
12962
12963   if (game.magic_wall_active)
12964   {
12965     if (!(game.magic_wall_time_left % 4))
12966     {
12967       int element = Feld[magic_wall_x][magic_wall_y];
12968
12969       if (element == EL_BD_MAGIC_WALL_FULL ||
12970           element == EL_BD_MAGIC_WALL_ACTIVE ||
12971           element == EL_BD_MAGIC_WALL_EMPTYING)
12972         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12973       else if (element == EL_DC_MAGIC_WALL_FULL ||
12974                element == EL_DC_MAGIC_WALL_ACTIVE ||
12975                element == EL_DC_MAGIC_WALL_EMPTYING)
12976         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12977       else
12978         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12979     }
12980
12981     if (game.magic_wall_time_left > 0)
12982     {
12983       game.magic_wall_time_left--;
12984
12985       if (!game.magic_wall_time_left)
12986       {
12987         SCAN_PLAYFIELD(x, y)
12988         {
12989           element = Feld[x][y];
12990
12991           if (element == EL_MAGIC_WALL_ACTIVE ||
12992               element == EL_MAGIC_WALL_FULL)
12993           {
12994             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12995             TEST_DrawLevelField(x, y);
12996           }
12997           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12998                    element == EL_BD_MAGIC_WALL_FULL)
12999           {
13000             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13001             TEST_DrawLevelField(x, y);
13002           }
13003           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13004                    element == EL_DC_MAGIC_WALL_FULL)
13005           {
13006             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13007             TEST_DrawLevelField(x, y);
13008           }
13009         }
13010
13011         game.magic_wall_active = FALSE;
13012       }
13013     }
13014   }
13015
13016   if (game.light_time_left > 0)
13017   {
13018     game.light_time_left--;
13019
13020     if (game.light_time_left == 0)
13021       RedrawAllLightSwitchesAndInvisibleElements();
13022   }
13023
13024   if (game.timegate_time_left > 0)
13025   {
13026     game.timegate_time_left--;
13027
13028     if (game.timegate_time_left == 0)
13029       CloseAllOpenTimegates();
13030   }
13031
13032   if (game.lenses_time_left > 0)
13033   {
13034     game.lenses_time_left--;
13035
13036     if (game.lenses_time_left == 0)
13037       RedrawAllInvisibleElementsForLenses();
13038   }
13039
13040   if (game.magnify_time_left > 0)
13041   {
13042     game.magnify_time_left--;
13043
13044     if (game.magnify_time_left == 0)
13045       RedrawAllInvisibleElementsForMagnifier();
13046   }
13047
13048   for (i = 0; i < MAX_PLAYERS; i++)
13049   {
13050     struct PlayerInfo *player = &stored_player[i];
13051
13052     if (SHIELD_ON(player))
13053     {
13054       if (player->shield_deadly_time_left)
13055         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13056       else if (player->shield_normal_time_left)
13057         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13058     }
13059   }
13060
13061 #if USE_DELAYED_GFX_REDRAW
13062   SCAN_PLAYFIELD(x, y)
13063   {
13064 #if 1
13065     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13066 #else
13067     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13068         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13069 #endif
13070     {
13071       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13072          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13073
13074       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13075         DrawLevelField(x, y);
13076
13077       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13078         DrawLevelFieldCrumbledSand(x, y);
13079
13080       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13081         DrawLevelFieldCrumbledSandNeighbours(x, y);
13082
13083       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13084         DrawTwinkleOnField(x, y);
13085     }
13086
13087     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13088   }
13089 #endif
13090
13091   CheckLevelTime();
13092
13093   DrawAllPlayers();
13094   PlayAllPlayersSound();
13095
13096   if (options.debug)                    /* calculate frames per second */
13097   {
13098     static unsigned long fps_counter = 0;
13099     static int fps_frames = 0;
13100     unsigned long fps_delay_ms = Counter() - fps_counter;
13101
13102     fps_frames++;
13103
13104     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13105     {
13106       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13107
13108       fps_frames = 0;
13109       fps_counter = Counter();
13110     }
13111
13112     redraw_mask |= REDRAW_FPS;
13113   }
13114
13115   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13116
13117   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13118   {
13119     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13120
13121     local_player->show_envelope = 0;
13122   }
13123
13124 #if 0
13125   debug_print_timestamp(0, "stop main loop profiling ");
13126   printf("----------------------------------------------------------\n");
13127 #endif
13128
13129   /* use random number generator in every frame to make it less predictable */
13130   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13131     RND(1);
13132 }
13133
13134 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13135 {
13136   int min_x = x, min_y = y, max_x = x, max_y = y;
13137   int i;
13138
13139   for (i = 0; i < MAX_PLAYERS; i++)
13140   {
13141     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13142
13143     if (!stored_player[i].active || &stored_player[i] == player)
13144       continue;
13145
13146     min_x = MIN(min_x, jx);
13147     min_y = MIN(min_y, jy);
13148     max_x = MAX(max_x, jx);
13149     max_y = MAX(max_y, jy);
13150   }
13151
13152   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13153 }
13154
13155 static boolean AllPlayersInVisibleScreen()
13156 {
13157   int i;
13158
13159   for (i = 0; i < MAX_PLAYERS; i++)
13160   {
13161     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13162
13163     if (!stored_player[i].active)
13164       continue;
13165
13166     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13167       return FALSE;
13168   }
13169
13170   return TRUE;
13171 }
13172
13173 void ScrollLevel(int dx, int dy)
13174 {
13175 #if 0
13176   /* (directly solved in BlitBitmap() now) */
13177   static Bitmap *bitmap_db_field2 = NULL;
13178   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13179   int x, y;
13180 #else
13181   int x, y;
13182 #endif
13183
13184 #if 0
13185   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13186   /* only horizontal XOR vertical scroll direction allowed */
13187   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13188     return;
13189 #endif
13190
13191 #if 0
13192   /* (directly solved in BlitBitmap() now) */
13193   if (bitmap_db_field2 == NULL)
13194     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13195
13196   /* needed when blitting directly to same bitmap -- should not be needed with
13197      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13198   BlitBitmap(drawto_field, bitmap_db_field2,
13199              FX + TILEX * (dx == -1) - softscroll_offset,
13200              FY + TILEY * (dy == -1) - softscroll_offset,
13201              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13202              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13203              FX + TILEX * (dx == 1) - softscroll_offset,
13204              FY + TILEY * (dy == 1) - softscroll_offset);
13205   BlitBitmap(bitmap_db_field2, drawto_field,
13206              FX + TILEX * (dx == 1) - softscroll_offset,
13207              FY + TILEY * (dy == 1) - softscroll_offset,
13208              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13209              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13210              FX + TILEX * (dx == 1) - softscroll_offset,
13211              FY + TILEY * (dy == 1) - softscroll_offset);
13212
13213 #else
13214
13215 #if 0
13216   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13217   int xsize = (BX2 - BX1 + 1);
13218   int ysize = (BY2 - BY1 + 1);
13219   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13220   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13221   int step  = (start < end ? +1 : -1);
13222
13223   for (i = start; i != end; i += step)
13224   {
13225     BlitBitmap(drawto_field, drawto_field,
13226                FX + TILEX * (dx != 0 ? i + step : 0),
13227                FY + TILEY * (dy != 0 ? i + step : 0),
13228                TILEX * (dx != 0 ? 1 : xsize),
13229                TILEY * (dy != 0 ? 1 : ysize),
13230                FX + TILEX * (dx != 0 ? i : 0),
13231                FY + TILEY * (dy != 0 ? i : 0));
13232   }
13233
13234 #else
13235
13236   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13237
13238   BlitBitmap(drawto_field, drawto_field,
13239              FX + TILEX * (dx == -1) - softscroll_offset,
13240              FY + TILEY * (dy == -1) - softscroll_offset,
13241              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13242              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13243              FX + TILEX * (dx == 1) - softscroll_offset,
13244              FY + TILEY * (dy == 1) - softscroll_offset);
13245 #endif
13246 #endif
13247
13248   if (dx != 0)
13249   {
13250     x = (dx == 1 ? BX1 : BX2);
13251     for (y = BY1; y <= BY2; y++)
13252       DrawScreenField(x, y);
13253   }
13254
13255   if (dy != 0)
13256   {
13257     y = (dy == 1 ? BY1 : BY2);
13258     for (x = BX1; x <= BX2; x++)
13259       DrawScreenField(x, y);
13260   }
13261
13262   redraw_mask |= REDRAW_FIELD;
13263 }
13264
13265 static boolean canFallDown(struct PlayerInfo *player)
13266 {
13267   int jx = player->jx, jy = player->jy;
13268
13269   return (IN_LEV_FIELD(jx, jy + 1) &&
13270           (IS_FREE(jx, jy + 1) ||
13271            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13272           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13273           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13274 }
13275
13276 static boolean canPassField(int x, int y, int move_dir)
13277 {
13278   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13279   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13280   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13281   int nextx = x + dx;
13282   int nexty = y + dy;
13283   int element = Feld[x][y];
13284
13285   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13286           !CAN_MOVE(element) &&
13287           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13288           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13289           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13290 }
13291
13292 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13293 {
13294   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13295   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13296   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13297   int newx = x + dx;
13298   int newy = y + dy;
13299
13300   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13301           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13302           (IS_DIGGABLE(Feld[newx][newy]) ||
13303            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13304            canPassField(newx, newy, move_dir)));
13305 }
13306
13307 static void CheckGravityMovement(struct PlayerInfo *player)
13308 {
13309 #if USE_PLAYER_GRAVITY
13310   if (player->gravity && !player->programmed_action)
13311 #else
13312   if (game.gravity && !player->programmed_action)
13313 #endif
13314   {
13315     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13316     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13317     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13318     int jx = player->jx, jy = player->jy;
13319     boolean player_is_moving_to_valid_field =
13320       (!player_is_snapping &&
13321        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13322         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13323     boolean player_can_fall_down = canFallDown(player);
13324
13325     if (player_can_fall_down &&
13326         !player_is_moving_to_valid_field)
13327       player->programmed_action = MV_DOWN;
13328   }
13329 }
13330
13331 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13332 {
13333   return CheckGravityMovement(player);
13334
13335 #if USE_PLAYER_GRAVITY
13336   if (player->gravity && !player->programmed_action)
13337 #else
13338   if (game.gravity && !player->programmed_action)
13339 #endif
13340   {
13341     int jx = player->jx, jy = player->jy;
13342     boolean field_under_player_is_free =
13343       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13344     boolean player_is_standing_on_valid_field =
13345       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13346        (IS_WALKABLE(Feld[jx][jy]) &&
13347         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13348
13349     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13350       player->programmed_action = MV_DOWN;
13351   }
13352 }
13353
13354 /*
13355   MovePlayerOneStep()
13356   -----------------------------------------------------------------------------
13357   dx, dy:               direction (non-diagonal) to try to move the player to
13358   real_dx, real_dy:     direction as read from input device (can be diagonal)
13359 */
13360
13361 boolean MovePlayerOneStep(struct PlayerInfo *player,
13362                           int dx, int dy, int real_dx, int real_dy)
13363 {
13364   int jx = player->jx, jy = player->jy;
13365   int new_jx = jx + dx, new_jy = jy + dy;
13366 #if !USE_FIXED_DONT_RUN_INTO
13367   int element;
13368 #endif
13369   int can_move;
13370   boolean player_can_move = !player->cannot_move;
13371
13372   if (!player->active || (!dx && !dy))
13373     return MP_NO_ACTION;
13374
13375   player->MovDir = (dx < 0 ? MV_LEFT :
13376                     dx > 0 ? MV_RIGHT :
13377                     dy < 0 ? MV_UP :
13378                     dy > 0 ? MV_DOWN :  MV_NONE);
13379
13380   if (!IN_LEV_FIELD(new_jx, new_jy))
13381     return MP_NO_ACTION;
13382
13383   if (!player_can_move)
13384   {
13385     if (player->MovPos == 0)
13386     {
13387       player->is_moving = FALSE;
13388       player->is_digging = FALSE;
13389       player->is_collecting = FALSE;
13390       player->is_snapping = FALSE;
13391       player->is_pushing = FALSE;
13392     }
13393   }
13394
13395 #if 1
13396   if (!options.network && game.centered_player_nr == -1 &&
13397       !AllPlayersInSight(player, new_jx, new_jy))
13398     return MP_NO_ACTION;
13399 #else
13400   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13401     return MP_NO_ACTION;
13402 #endif
13403
13404 #if !USE_FIXED_DONT_RUN_INTO
13405   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13406
13407   /* (moved to DigField()) */
13408   if (player_can_move && DONT_RUN_INTO(element))
13409   {
13410     if (element == EL_ACID && dx == 0 && dy == 1)
13411     {
13412       SplashAcid(new_jx, new_jy);
13413       Feld[jx][jy] = EL_PLAYER_1;
13414       InitMovingField(jx, jy, MV_DOWN);
13415       Store[jx][jy] = EL_ACID;
13416       ContinueMoving(jx, jy);
13417       BuryPlayer(player);
13418     }
13419     else
13420       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13421
13422     return MP_MOVING;
13423   }
13424 #endif
13425
13426   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13427   if (can_move != MP_MOVING)
13428     return can_move;
13429
13430   /* check if DigField() has caused relocation of the player */
13431   if (player->jx != jx || player->jy != jy)
13432     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13433
13434   StorePlayer[jx][jy] = 0;
13435   player->last_jx = jx;
13436   player->last_jy = jy;
13437   player->jx = new_jx;
13438   player->jy = new_jy;
13439   StorePlayer[new_jx][new_jy] = player->element_nr;
13440
13441   if (player->move_delay_value_next != -1)
13442   {
13443     player->move_delay_value = player->move_delay_value_next;
13444     player->move_delay_value_next = -1;
13445   }
13446
13447   player->MovPos =
13448     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13449
13450   player->step_counter++;
13451
13452   PlayerVisit[jx][jy] = FrameCounter;
13453
13454 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13455   player->is_moving = TRUE;
13456 #endif
13457
13458 #if 1
13459   /* should better be called in MovePlayer(), but this breaks some tapes */
13460   ScrollPlayer(player, SCROLL_INIT);
13461 #endif
13462
13463   return MP_MOVING;
13464 }
13465
13466 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13467 {
13468   int jx = player->jx, jy = player->jy;
13469   int old_jx = jx, old_jy = jy;
13470   int moved = MP_NO_ACTION;
13471
13472   if (!player->active)
13473     return FALSE;
13474
13475   if (!dx && !dy)
13476   {
13477     if (player->MovPos == 0)
13478     {
13479       player->is_moving = FALSE;
13480       player->is_digging = FALSE;
13481       player->is_collecting = FALSE;
13482       player->is_snapping = FALSE;
13483       player->is_pushing = FALSE;
13484     }
13485
13486     return FALSE;
13487   }
13488
13489   if (player->move_delay > 0)
13490     return FALSE;
13491
13492   player->move_delay = -1;              /* set to "uninitialized" value */
13493
13494   /* store if player is automatically moved to next field */
13495   player->is_auto_moving = (player->programmed_action != MV_NONE);
13496
13497   /* remove the last programmed player action */
13498   player->programmed_action = 0;
13499
13500   if (player->MovPos)
13501   {
13502     /* should only happen if pre-1.2 tape recordings are played */
13503     /* this is only for backward compatibility */
13504
13505     int original_move_delay_value = player->move_delay_value;
13506
13507 #if DEBUG
13508     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13509            tape.counter);
13510 #endif
13511
13512     /* scroll remaining steps with finest movement resolution */
13513     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13514
13515     while (player->MovPos)
13516     {
13517       ScrollPlayer(player, SCROLL_GO_ON);
13518       ScrollScreen(NULL, SCROLL_GO_ON);
13519
13520       AdvanceFrameAndPlayerCounters(player->index_nr);
13521
13522       DrawAllPlayers();
13523       BackToFront();
13524     }
13525
13526     player->move_delay_value = original_move_delay_value;
13527   }
13528
13529   player->is_active = FALSE;
13530
13531   if (player->last_move_dir & MV_HORIZONTAL)
13532   {
13533     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13534       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13535   }
13536   else
13537   {
13538     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13539       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13540   }
13541
13542 #if USE_FIXED_BORDER_RUNNING_GFX
13543   if (!moved && !player->is_active)
13544   {
13545     player->is_moving = FALSE;
13546     player->is_digging = FALSE;
13547     player->is_collecting = FALSE;
13548     player->is_snapping = FALSE;
13549     player->is_pushing = FALSE;
13550   }
13551 #endif
13552
13553   jx = player->jx;
13554   jy = player->jy;
13555
13556 #if 1
13557   if (moved & MP_MOVING && !ScreenMovPos &&
13558       (player->index_nr == game.centered_player_nr ||
13559        game.centered_player_nr == -1))
13560 #else
13561   if (moved & MP_MOVING && !ScreenMovPos &&
13562       (player == local_player || !options.network))
13563 #endif
13564   {
13565     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13566     int offset = game.scroll_delay_value;
13567
13568     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13569     {
13570       /* actual player has left the screen -- scroll in that direction */
13571       if (jx != old_jx)         /* player has moved horizontally */
13572         scroll_x += (jx - old_jx);
13573       else                      /* player has moved vertically */
13574         scroll_y += (jy - old_jy);
13575     }
13576     else
13577     {
13578       if (jx != old_jx)         /* player has moved horizontally */
13579       {
13580         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13581             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13582           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13583
13584         /* don't scroll over playfield boundaries */
13585         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13586           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13587
13588         /* don't scroll more than one field at a time */
13589         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13590
13591         /* don't scroll against the player's moving direction */
13592         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13593             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13594           scroll_x = old_scroll_x;
13595       }
13596       else                      /* player has moved vertically */
13597       {
13598         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13599             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13600           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13601
13602         /* don't scroll over playfield boundaries */
13603         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13604           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13605
13606         /* don't scroll more than one field at a time */
13607         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13608
13609         /* don't scroll against the player's moving direction */
13610         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13611             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13612           scroll_y = old_scroll_y;
13613       }
13614     }
13615
13616     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13617     {
13618 #if 1
13619       if (!options.network && game.centered_player_nr == -1 &&
13620           !AllPlayersInVisibleScreen())
13621       {
13622         scroll_x = old_scroll_x;
13623         scroll_y = old_scroll_y;
13624       }
13625       else
13626 #else
13627       if (!options.network && !AllPlayersInVisibleScreen())
13628       {
13629         scroll_x = old_scroll_x;
13630         scroll_y = old_scroll_y;
13631       }
13632       else
13633 #endif
13634       {
13635         ScrollScreen(player, SCROLL_INIT);
13636         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13637       }
13638     }
13639   }
13640
13641   player->StepFrame = 0;
13642
13643   if (moved & MP_MOVING)
13644   {
13645     if (old_jx != jx && old_jy == jy)
13646       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13647     else if (old_jx == jx && old_jy != jy)
13648       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13649
13650     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13651
13652     player->last_move_dir = player->MovDir;
13653     player->is_moving = TRUE;
13654     player->is_snapping = FALSE;
13655     player->is_switching = FALSE;
13656     player->is_dropping = FALSE;
13657     player->is_dropping_pressed = FALSE;
13658     player->drop_pressed_delay = 0;
13659
13660 #if 0
13661     /* should better be called here than above, but this breaks some tapes */
13662     ScrollPlayer(player, SCROLL_INIT);
13663 #endif
13664   }
13665   else
13666   {
13667     CheckGravityMovementWhenNotMoving(player);
13668
13669     player->is_moving = FALSE;
13670
13671     /* at this point, the player is allowed to move, but cannot move right now
13672        (e.g. because of something blocking the way) -- ensure that the player
13673        is also allowed to move in the next frame (in old versions before 3.1.1,
13674        the player was forced to wait again for eight frames before next try) */
13675
13676     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13677       player->move_delay = 0;   /* allow direct movement in the next frame */
13678   }
13679
13680   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13681     player->move_delay = player->move_delay_value;
13682
13683   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13684   {
13685     TestIfPlayerTouchesBadThing(jx, jy);
13686     TestIfPlayerTouchesCustomElement(jx, jy);
13687   }
13688
13689   if (!player->active)
13690     RemovePlayer(player);
13691
13692   return moved;
13693 }
13694
13695 void ScrollPlayer(struct PlayerInfo *player, int mode)
13696 {
13697   int jx = player->jx, jy = player->jy;
13698   int last_jx = player->last_jx, last_jy = player->last_jy;
13699   int move_stepsize = TILEX / player->move_delay_value;
13700
13701 #if USE_NEW_PLAYER_SPEED
13702   if (!player->active)
13703     return;
13704
13705   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13706     return;
13707 #else
13708   if (!player->active || player->MovPos == 0)
13709     return;
13710 #endif
13711
13712   if (mode == SCROLL_INIT)
13713   {
13714     player->actual_frame_counter = FrameCounter;
13715     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13716
13717     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13718         Feld[last_jx][last_jy] == EL_EMPTY)
13719     {
13720       int last_field_block_delay = 0;   /* start with no blocking at all */
13721       int block_delay_adjustment = player->block_delay_adjustment;
13722
13723       /* if player blocks last field, add delay for exactly one move */
13724       if (player->block_last_field)
13725       {
13726         last_field_block_delay += player->move_delay_value;
13727
13728         /* when blocking enabled, prevent moving up despite gravity */
13729 #if USE_PLAYER_GRAVITY
13730         if (player->gravity && player->MovDir == MV_UP)
13731           block_delay_adjustment = -1;
13732 #else
13733         if (game.gravity && player->MovDir == MV_UP)
13734           block_delay_adjustment = -1;
13735 #endif
13736       }
13737
13738       /* add block delay adjustment (also possible when not blocking) */
13739       last_field_block_delay += block_delay_adjustment;
13740
13741       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13742       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13743     }
13744
13745 #if USE_NEW_PLAYER_SPEED
13746     if (player->MovPos != 0)    /* player has not yet reached destination */
13747       return;
13748 #else
13749     return;
13750 #endif
13751   }
13752   else if (!FrameReached(&player->actual_frame_counter, 1))
13753     return;
13754
13755 #if USE_NEW_PLAYER_SPEED
13756   if (player->MovPos != 0)
13757   {
13758     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13759     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13760
13761     /* before DrawPlayer() to draw correct player graphic for this case */
13762     if (player->MovPos == 0)
13763       CheckGravityMovement(player);
13764   }
13765 #else
13766   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13767   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13768
13769   /* before DrawPlayer() to draw correct player graphic for this case */
13770   if (player->MovPos == 0)
13771     CheckGravityMovement(player);
13772 #endif
13773
13774   if (player->MovPos == 0)      /* player reached destination field */
13775   {
13776     if (player->move_delay_reset_counter > 0)
13777     {
13778       player->move_delay_reset_counter--;
13779
13780       if (player->move_delay_reset_counter == 0)
13781       {
13782         /* continue with normal speed after quickly moving through gate */
13783         HALVE_PLAYER_SPEED(player);
13784
13785         /* be able to make the next move without delay */
13786         player->move_delay = 0;
13787       }
13788     }
13789
13790     player->last_jx = jx;
13791     player->last_jy = jy;
13792
13793     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13794         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13795 #if 1
13796         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13797 #endif
13798         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13799         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13800 #if 1
13801         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13802 #endif
13803         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13804         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13805     {
13806       DrawPlayer(player);       /* needed here only to cleanup last field */
13807       RemovePlayer(player);
13808
13809       if (local_player->friends_still_needed == 0 ||
13810           IS_SP_ELEMENT(Feld[jx][jy]))
13811         PlayerWins(player);
13812     }
13813
13814     /* this breaks one level: "machine", level 000 */
13815     {
13816       int move_direction = player->MovDir;
13817       int enter_side = MV_DIR_OPPOSITE(move_direction);
13818       int leave_side = move_direction;
13819       int old_jx = last_jx;
13820       int old_jy = last_jy;
13821       int old_element = Feld[old_jx][old_jy];
13822       int new_element = Feld[jx][jy];
13823
13824       if (IS_CUSTOM_ELEMENT(old_element))
13825         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13826                                    CE_LEFT_BY_PLAYER,
13827                                    player->index_bit, leave_side);
13828
13829       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13830                                           CE_PLAYER_LEAVES_X,
13831                                           player->index_bit, leave_side);
13832
13833       if (IS_CUSTOM_ELEMENT(new_element))
13834         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13835                                    player->index_bit, enter_side);
13836
13837       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13838                                           CE_PLAYER_ENTERS_X,
13839                                           player->index_bit, enter_side);
13840
13841 #if USE_FIX_CE_ACTION_WITH_PLAYER
13842       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13843                                         CE_MOVE_OF_X, move_direction);
13844 #else
13845       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13846                                         CE_MOVE_OF_X, move_direction);
13847 #endif
13848     }
13849
13850     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13851     {
13852       TestIfPlayerTouchesBadThing(jx, jy);
13853       TestIfPlayerTouchesCustomElement(jx, jy);
13854
13855       /* needed because pushed element has not yet reached its destination,
13856          so it would trigger a change event at its previous field location */
13857       if (!player->is_pushing)
13858         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13859
13860       if (!player->active)
13861         RemovePlayer(player);
13862     }
13863
13864     if (!local_player->LevelSolved && level.use_step_counter)
13865     {
13866       int i;
13867
13868       TimePlayed++;
13869
13870       if (TimeLeft > 0)
13871       {
13872         TimeLeft--;
13873
13874         if (TimeLeft <= 10 && setup.time_limit)
13875           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13876
13877 #if 1
13878         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13879
13880         DisplayGameControlValues();
13881 #else
13882         DrawGameValue_Time(TimeLeft);
13883 #endif
13884
13885         if (!TimeLeft && setup.time_limit)
13886           for (i = 0; i < MAX_PLAYERS; i++)
13887             KillPlayer(&stored_player[i]);
13888       }
13889 #if 1
13890       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13891       {
13892         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13893
13894         DisplayGameControlValues();
13895       }
13896 #else
13897       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13898         DrawGameValue_Time(TimePlayed);
13899 #endif
13900     }
13901
13902     if (tape.single_step && tape.recording && !tape.pausing &&
13903         !player->programmed_action)
13904       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13905   }
13906 }
13907
13908 void ScrollScreen(struct PlayerInfo *player, int mode)
13909 {
13910   static unsigned long screen_frame_counter = 0;
13911
13912   if (mode == SCROLL_INIT)
13913   {
13914     /* set scrolling step size according to actual player's moving speed */
13915     ScrollStepSize = TILEX / player->move_delay_value;
13916
13917     screen_frame_counter = FrameCounter;
13918     ScreenMovDir = player->MovDir;
13919     ScreenMovPos = player->MovPos;
13920     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13921     return;
13922   }
13923   else if (!FrameReached(&screen_frame_counter, 1))
13924     return;
13925
13926   if (ScreenMovPos)
13927   {
13928     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13929     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13930     redraw_mask |= REDRAW_FIELD;
13931   }
13932   else
13933     ScreenMovDir = MV_NONE;
13934 }
13935
13936 void TestIfPlayerTouchesCustomElement(int x, int y)
13937 {
13938   static int xy[4][2] =
13939   {
13940     { 0, -1 },
13941     { -1, 0 },
13942     { +1, 0 },
13943     { 0, +1 }
13944   };
13945   static int trigger_sides[4][2] =
13946   {
13947     /* center side       border side */
13948     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13949     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13950     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13951     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13952   };
13953   static int touch_dir[4] =
13954   {
13955     MV_LEFT | MV_RIGHT,
13956     MV_UP   | MV_DOWN,
13957     MV_UP   | MV_DOWN,
13958     MV_LEFT | MV_RIGHT
13959   };
13960   int center_element = Feld[x][y];      /* should always be non-moving! */
13961   int i;
13962
13963   for (i = 0; i < NUM_DIRECTIONS; i++)
13964   {
13965     int xx = x + xy[i][0];
13966     int yy = y + xy[i][1];
13967     int center_side = trigger_sides[i][0];
13968     int border_side = trigger_sides[i][1];
13969     int border_element;
13970
13971     if (!IN_LEV_FIELD(xx, yy))
13972       continue;
13973
13974     if (IS_PLAYER(x, y))                /* player found at center element */
13975     {
13976       struct PlayerInfo *player = PLAYERINFO(x, y);
13977
13978       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13979         border_element = Feld[xx][yy];          /* may be moving! */
13980       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13981         border_element = Feld[xx][yy];
13982       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13983         border_element = MovingOrBlocked2Element(xx, yy);
13984       else
13985         continue;               /* center and border element do not touch */
13986
13987       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13988                                  player->index_bit, border_side);
13989       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13990                                           CE_PLAYER_TOUCHES_X,
13991                                           player->index_bit, border_side);
13992
13993 #if USE_FIX_CE_ACTION_WITH_PLAYER
13994       {
13995         /* use player element that is initially defined in the level playfield,
13996            not the player element that corresponds to the runtime player number
13997            (example: a level that contains EL_PLAYER_3 as the only player would
13998            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13999         int player_element = PLAYERINFO(x, y)->initial_element;
14000
14001         CheckElementChangeBySide(xx, yy, border_element, player_element,
14002                                  CE_TOUCHING_X, border_side);
14003       }
14004 #endif
14005     }
14006     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14007     {
14008       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14009
14010       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14011       {
14012         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14013           continue;             /* center and border element do not touch */
14014       }
14015
14016       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14017                                  player->index_bit, center_side);
14018       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14019                                           CE_PLAYER_TOUCHES_X,
14020                                           player->index_bit, center_side);
14021
14022 #if USE_FIX_CE_ACTION_WITH_PLAYER
14023       {
14024         /* use player element that is initially defined in the level playfield,
14025            not the player element that corresponds to the runtime player number
14026            (example: a level that contains EL_PLAYER_3 as the only player would
14027            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14028         int player_element = PLAYERINFO(xx, yy)->initial_element;
14029
14030         CheckElementChangeBySide(x, y, center_element, player_element,
14031                                  CE_TOUCHING_X, center_side);
14032       }
14033 #endif
14034
14035       break;
14036     }
14037   }
14038 }
14039
14040 #if USE_ELEMENT_TOUCHING_BUGFIX
14041
14042 void TestIfElementTouchesCustomElement(int x, int y)
14043 {
14044   static int xy[4][2] =
14045   {
14046     { 0, -1 },
14047     { -1, 0 },
14048     { +1, 0 },
14049     { 0, +1 }
14050   };
14051   static int trigger_sides[4][2] =
14052   {
14053     /* center side      border side */
14054     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14055     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14056     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14057     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14058   };
14059   static int touch_dir[4] =
14060   {
14061     MV_LEFT | MV_RIGHT,
14062     MV_UP   | MV_DOWN,
14063     MV_UP   | MV_DOWN,
14064     MV_LEFT | MV_RIGHT
14065   };
14066   boolean change_center_element = FALSE;
14067   int center_element = Feld[x][y];      /* should always be non-moving! */
14068   int border_element_old[NUM_DIRECTIONS];
14069   int i;
14070
14071   for (i = 0; i < NUM_DIRECTIONS; i++)
14072   {
14073     int xx = x + xy[i][0];
14074     int yy = y + xy[i][1];
14075     int border_element;
14076
14077     border_element_old[i] = -1;
14078
14079     if (!IN_LEV_FIELD(xx, yy))
14080       continue;
14081
14082     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14083       border_element = Feld[xx][yy];    /* may be moving! */
14084     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14085       border_element = Feld[xx][yy];
14086     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14087       border_element = MovingOrBlocked2Element(xx, yy);
14088     else
14089       continue;                 /* center and border element do not touch */
14090
14091     border_element_old[i] = border_element;
14092   }
14093
14094   for (i = 0; i < NUM_DIRECTIONS; i++)
14095   {
14096     int xx = x + xy[i][0];
14097     int yy = y + xy[i][1];
14098     int center_side = trigger_sides[i][0];
14099     int border_element = border_element_old[i];
14100
14101     if (border_element == -1)
14102       continue;
14103
14104     /* check for change of border element */
14105     CheckElementChangeBySide(xx, yy, border_element, center_element,
14106                              CE_TOUCHING_X, center_side);
14107
14108     /* (center element cannot be player, so we dont have to check this here) */
14109   }
14110
14111   for (i = 0; i < NUM_DIRECTIONS; i++)
14112   {
14113     int xx = x + xy[i][0];
14114     int yy = y + xy[i][1];
14115     int border_side = trigger_sides[i][1];
14116     int border_element = border_element_old[i];
14117
14118     if (border_element == -1)
14119       continue;
14120
14121     /* check for change of center element (but change it only once) */
14122     if (!change_center_element)
14123       change_center_element =
14124         CheckElementChangeBySide(x, y, center_element, border_element,
14125                                  CE_TOUCHING_X, border_side);
14126
14127 #if USE_FIX_CE_ACTION_WITH_PLAYER
14128     if (IS_PLAYER(xx, yy))
14129     {
14130       /* use player element that is initially defined in the level playfield,
14131          not the player element that corresponds to the runtime player number
14132          (example: a level that contains EL_PLAYER_3 as the only player would
14133          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14134       int player_element = PLAYERINFO(xx, yy)->initial_element;
14135
14136       CheckElementChangeBySide(x, y, center_element, player_element,
14137                                CE_TOUCHING_X, border_side);
14138     }
14139 #endif
14140   }
14141 }
14142
14143 #else
14144
14145 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14146 {
14147   static int xy[4][2] =
14148   {
14149     { 0, -1 },
14150     { -1, 0 },
14151     { +1, 0 },
14152     { 0, +1 }
14153   };
14154   static int trigger_sides[4][2] =
14155   {
14156     /* center side      border side */
14157     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14158     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14159     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14160     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14161   };
14162   static int touch_dir[4] =
14163   {
14164     MV_LEFT | MV_RIGHT,
14165     MV_UP   | MV_DOWN,
14166     MV_UP   | MV_DOWN,
14167     MV_LEFT | MV_RIGHT
14168   };
14169   boolean change_center_element = FALSE;
14170   int center_element = Feld[x][y];      /* should always be non-moving! */
14171   int i;
14172
14173   for (i = 0; i < NUM_DIRECTIONS; i++)
14174   {
14175     int xx = x + xy[i][0];
14176     int yy = y + xy[i][1];
14177     int center_side = trigger_sides[i][0];
14178     int border_side = trigger_sides[i][1];
14179     int border_element;
14180
14181     if (!IN_LEV_FIELD(xx, yy))
14182       continue;
14183
14184     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14185       border_element = Feld[xx][yy];    /* may be moving! */
14186     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14187       border_element = Feld[xx][yy];
14188     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14189       border_element = MovingOrBlocked2Element(xx, yy);
14190     else
14191       continue;                 /* center and border element do not touch */
14192
14193     /* check for change of center element (but change it only once) */
14194     if (!change_center_element)
14195       change_center_element =
14196         CheckElementChangeBySide(x, y, center_element, border_element,
14197                                  CE_TOUCHING_X, border_side);
14198
14199     /* check for change of border element */
14200     CheckElementChangeBySide(xx, yy, border_element, center_element,
14201                              CE_TOUCHING_X, center_side);
14202   }
14203 }
14204
14205 #endif
14206
14207 void TestIfElementHitsCustomElement(int x, int y, int direction)
14208 {
14209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14211   int hitx = x + dx, hity = y + dy;
14212   int hitting_element = Feld[x][y];
14213   int touched_element;
14214
14215   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14216     return;
14217
14218   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14219                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14220
14221   if (IN_LEV_FIELD(hitx, hity))
14222   {
14223     int opposite_direction = MV_DIR_OPPOSITE(direction);
14224     int hitting_side = direction;
14225     int touched_side = opposite_direction;
14226     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14227                           MovDir[hitx][hity] != direction ||
14228                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14229
14230     object_hit = TRUE;
14231
14232     if (object_hit)
14233     {
14234       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14235                                CE_HITTING_X, touched_side);
14236
14237       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238                                CE_HIT_BY_X, hitting_side);
14239
14240       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14241                                CE_HIT_BY_SOMETHING, opposite_direction);
14242
14243 #if USE_FIX_CE_ACTION_WITH_PLAYER
14244       if (IS_PLAYER(hitx, hity))
14245       {
14246         /* use player element that is initially defined in the level playfield,
14247            not the player element that corresponds to the runtime player number
14248            (example: a level that contains EL_PLAYER_3 as the only player would
14249            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14250         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14251
14252         CheckElementChangeBySide(x, y, hitting_element, player_element,
14253                                  CE_HITTING_X, touched_side);
14254       }
14255 #endif
14256     }
14257   }
14258
14259   /* "hitting something" is also true when hitting the playfield border */
14260   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14261                            CE_HITTING_SOMETHING, direction);
14262 }
14263
14264 #if 0
14265 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14266 {
14267   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14268   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14269   int hitx = x + dx, hity = y + dy;
14270   int hitting_element = Feld[x][y];
14271   int touched_element;
14272 #if 0
14273   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14274                         !IS_FREE(hitx, hity) &&
14275                         (!IS_MOVING(hitx, hity) ||
14276                          MovDir[hitx][hity] != direction ||
14277                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14278 #endif
14279
14280   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14281     return;
14282
14283 #if 0
14284   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14285     return;
14286 #endif
14287
14288   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14289                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14290
14291   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14292                            EP_CAN_SMASH_EVERYTHING, direction);
14293
14294   if (IN_LEV_FIELD(hitx, hity))
14295   {
14296     int opposite_direction = MV_DIR_OPPOSITE(direction);
14297     int hitting_side = direction;
14298     int touched_side = opposite_direction;
14299 #if 0
14300     int touched_element = MovingOrBlocked2Element(hitx, hity);
14301 #endif
14302 #if 1
14303     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14304                           MovDir[hitx][hity] != direction ||
14305                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14306
14307     object_hit = TRUE;
14308 #endif
14309
14310     if (object_hit)
14311     {
14312       int i;
14313
14314       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14315                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14316
14317       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14318                                CE_OTHER_IS_SMASHING, touched_side);
14319
14320       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14321                                CE_OTHER_GETS_SMASHED, hitting_side);
14322     }
14323   }
14324 }
14325 #endif
14326
14327 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14328 {
14329   int i, kill_x = -1, kill_y = -1;
14330
14331   int bad_element = -1;
14332   static int test_xy[4][2] =
14333   {
14334     { 0, -1 },
14335     { -1, 0 },
14336     { +1, 0 },
14337     { 0, +1 }
14338   };
14339   static int test_dir[4] =
14340   {
14341     MV_UP,
14342     MV_LEFT,
14343     MV_RIGHT,
14344     MV_DOWN
14345   };
14346
14347   for (i = 0; i < NUM_DIRECTIONS; i++)
14348   {
14349     int test_x, test_y, test_move_dir, test_element;
14350
14351     test_x = good_x + test_xy[i][0];
14352     test_y = good_y + test_xy[i][1];
14353
14354     if (!IN_LEV_FIELD(test_x, test_y))
14355       continue;
14356
14357     test_move_dir =
14358       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14359
14360     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14361
14362     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14363        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14364     */
14365     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14366         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14367     {
14368       kill_x = test_x;
14369       kill_y = test_y;
14370       bad_element = test_element;
14371
14372       break;
14373     }
14374   }
14375
14376   if (kill_x != -1 || kill_y != -1)
14377   {
14378     if (IS_PLAYER(good_x, good_y))
14379     {
14380       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14381
14382       if (player->shield_deadly_time_left > 0 &&
14383           !IS_INDESTRUCTIBLE(bad_element))
14384         Bang(kill_x, kill_y);
14385       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14386         KillPlayer(player);
14387     }
14388     else
14389       Bang(good_x, good_y);
14390   }
14391 }
14392
14393 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14394 {
14395   int i, kill_x = -1, kill_y = -1;
14396   int bad_element = Feld[bad_x][bad_y];
14397   static int test_xy[4][2] =
14398   {
14399     { 0, -1 },
14400     { -1, 0 },
14401     { +1, 0 },
14402     { 0, +1 }
14403   };
14404   static int touch_dir[4] =
14405   {
14406     MV_LEFT | MV_RIGHT,
14407     MV_UP   | MV_DOWN,
14408     MV_UP   | MV_DOWN,
14409     MV_LEFT | MV_RIGHT
14410   };
14411   static int test_dir[4] =
14412   {
14413     MV_UP,
14414     MV_LEFT,
14415     MV_RIGHT,
14416     MV_DOWN
14417   };
14418
14419   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14420     return;
14421
14422   for (i = 0; i < NUM_DIRECTIONS; i++)
14423   {
14424     int test_x, test_y, test_move_dir, test_element;
14425
14426     test_x = bad_x + test_xy[i][0];
14427     test_y = bad_y + test_xy[i][1];
14428
14429     if (!IN_LEV_FIELD(test_x, test_y))
14430       continue;
14431
14432     test_move_dir =
14433       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14434
14435     test_element = Feld[test_x][test_y];
14436
14437     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14438        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14439     */
14440     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14441         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14442     {
14443       /* good thing is player or penguin that does not move away */
14444       if (IS_PLAYER(test_x, test_y))
14445       {
14446         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14447
14448         if (bad_element == EL_ROBOT && player->is_moving)
14449           continue;     /* robot does not kill player if he is moving */
14450
14451         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14452         {
14453           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14454             continue;           /* center and border element do not touch */
14455         }
14456
14457         kill_x = test_x;
14458         kill_y = test_y;
14459
14460         break;
14461       }
14462       else if (test_element == EL_PENGUIN)
14463       {
14464         kill_x = test_x;
14465         kill_y = test_y;
14466
14467         break;
14468       }
14469     }
14470   }
14471
14472   if (kill_x != -1 || kill_y != -1)
14473   {
14474     if (IS_PLAYER(kill_x, kill_y))
14475     {
14476       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14477
14478       if (player->shield_deadly_time_left > 0 &&
14479           !IS_INDESTRUCTIBLE(bad_element))
14480         Bang(bad_x, bad_y);
14481       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14482         KillPlayer(player);
14483     }
14484     else
14485       Bang(kill_x, kill_y);
14486   }
14487 }
14488
14489 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14490 {
14491   int bad_element = Feld[bad_x][bad_y];
14492   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14493   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14494   int test_x = bad_x + dx, test_y = bad_y + dy;
14495   int test_move_dir, test_element;
14496   int kill_x = -1, kill_y = -1;
14497
14498   if (!IN_LEV_FIELD(test_x, test_y))
14499     return;
14500
14501   test_move_dir =
14502     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14503
14504   test_element = Feld[test_x][test_y];
14505
14506   if (test_move_dir != bad_move_dir)
14507   {
14508     /* good thing can be player or penguin that does not move away */
14509     if (IS_PLAYER(test_x, test_y))
14510     {
14511       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14512
14513       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14514          player as being hit when he is moving towards the bad thing, because
14515          the "get hit by" condition would be lost after the player stops) */
14516       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14517         return;         /* player moves away from bad thing */
14518
14519       kill_x = test_x;
14520       kill_y = test_y;
14521     }
14522     else if (test_element == EL_PENGUIN)
14523     {
14524       kill_x = test_x;
14525       kill_y = test_y;
14526     }
14527   }
14528
14529   if (kill_x != -1 || kill_y != -1)
14530   {
14531     if (IS_PLAYER(kill_x, kill_y))
14532     {
14533       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14534
14535       if (player->shield_deadly_time_left > 0 &&
14536           !IS_INDESTRUCTIBLE(bad_element))
14537         Bang(bad_x, bad_y);
14538       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14539         KillPlayer(player);
14540     }
14541     else
14542       Bang(kill_x, kill_y);
14543   }
14544 }
14545
14546 void TestIfPlayerTouchesBadThing(int x, int y)
14547 {
14548   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14549 }
14550
14551 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14552 {
14553   TestIfGoodThingHitsBadThing(x, y, move_dir);
14554 }
14555
14556 void TestIfBadThingTouchesPlayer(int x, int y)
14557 {
14558   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14559 }
14560
14561 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14562 {
14563   TestIfBadThingHitsGoodThing(x, y, move_dir);
14564 }
14565
14566 void TestIfFriendTouchesBadThing(int x, int y)
14567 {
14568   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14569 }
14570
14571 void TestIfBadThingTouchesFriend(int x, int y)
14572 {
14573   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14574 }
14575
14576 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14577 {
14578   int i, kill_x = bad_x, kill_y = bad_y;
14579   static int xy[4][2] =
14580   {
14581     { 0, -1 },
14582     { -1, 0 },
14583     { +1, 0 },
14584     { 0, +1 }
14585   };
14586
14587   for (i = 0; i < NUM_DIRECTIONS; i++)
14588   {
14589     int x, y, element;
14590
14591     x = bad_x + xy[i][0];
14592     y = bad_y + xy[i][1];
14593     if (!IN_LEV_FIELD(x, y))
14594       continue;
14595
14596     element = Feld[x][y];
14597     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14598         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14599     {
14600       kill_x = x;
14601       kill_y = y;
14602       break;
14603     }
14604   }
14605
14606   if (kill_x != bad_x || kill_y != bad_y)
14607     Bang(bad_x, bad_y);
14608 }
14609
14610 void KillPlayer(struct PlayerInfo *player)
14611 {
14612   int jx = player->jx, jy = player->jy;
14613
14614   if (!player->active)
14615     return;
14616
14617 #if 0
14618   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14619          player->killed, player->active, player->reanimated);
14620 #endif
14621
14622   /* the following code was introduced to prevent an infinite loop when calling
14623      -> Bang()
14624      -> CheckTriggeredElementChangeExt()
14625      -> ExecuteCustomElementAction()
14626      -> KillPlayer()
14627      -> (infinitely repeating the above sequence of function calls)
14628      which occurs when killing the player while having a CE with the setting
14629      "kill player X when explosion of <player X>"; the solution using a new
14630      field "player->killed" was chosen for backwards compatibility, although
14631      clever use of the fields "player->active" etc. would probably also work */
14632 #if 1
14633   if (player->killed)
14634     return;
14635 #endif
14636
14637   player->killed = TRUE;
14638
14639   /* remove accessible field at the player's position */
14640   Feld[jx][jy] = EL_EMPTY;
14641
14642   /* deactivate shield (else Bang()/Explode() would not work right) */
14643   player->shield_normal_time_left = 0;
14644   player->shield_deadly_time_left = 0;
14645
14646 #if 0
14647   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14648          player->killed, player->active, player->reanimated);
14649 #endif
14650
14651   Bang(jx, jy);
14652
14653 #if 0
14654   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14655          player->killed, player->active, player->reanimated);
14656 #endif
14657
14658 #if USE_PLAYER_REANIMATION
14659 #if 1
14660   if (player->reanimated)       /* killed player may have been reanimated */
14661     player->killed = player->reanimated = FALSE;
14662   else
14663     BuryPlayer(player);
14664 #else
14665   if (player->killed)           /* player may have been reanimated */
14666     BuryPlayer(player);
14667 #endif
14668 #else
14669   BuryPlayer(player);
14670 #endif
14671 }
14672
14673 static void KillPlayerUnlessEnemyProtected(int x, int y)
14674 {
14675   if (!PLAYER_ENEMY_PROTECTED(x, y))
14676     KillPlayer(PLAYERINFO(x, y));
14677 }
14678
14679 static void KillPlayerUnlessExplosionProtected(int x, int y)
14680 {
14681   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14682     KillPlayer(PLAYERINFO(x, y));
14683 }
14684
14685 void BuryPlayer(struct PlayerInfo *player)
14686 {
14687   int jx = player->jx, jy = player->jy;
14688
14689   if (!player->active)
14690     return;
14691
14692   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14693   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14694
14695   player->GameOver = TRUE;
14696   RemovePlayer(player);
14697 }
14698
14699 void RemovePlayer(struct PlayerInfo *player)
14700 {
14701   int jx = player->jx, jy = player->jy;
14702   int i, found = FALSE;
14703
14704   player->present = FALSE;
14705   player->active = FALSE;
14706
14707   if (!ExplodeField[jx][jy])
14708     StorePlayer[jx][jy] = 0;
14709
14710   if (player->is_moving)
14711     TEST_DrawLevelField(player->last_jx, player->last_jy);
14712
14713   for (i = 0; i < MAX_PLAYERS; i++)
14714     if (stored_player[i].active)
14715       found = TRUE;
14716
14717   if (!found)
14718     AllPlayersGone = TRUE;
14719
14720   ExitX = ZX = jx;
14721   ExitY = ZY = jy;
14722 }
14723
14724 #if USE_NEW_SNAP_DELAY
14725 static void setFieldForSnapping(int x, int y, int element, int direction)
14726 {
14727   struct ElementInfo *ei = &element_info[element];
14728   int direction_bit = MV_DIR_TO_BIT(direction);
14729   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14730   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14731                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14732
14733   Feld[x][y] = EL_ELEMENT_SNAPPING;
14734   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14735
14736   ResetGfxAnimation(x, y);
14737
14738   GfxElement[x][y] = element;
14739   GfxAction[x][y] = action;
14740   GfxDir[x][y] = direction;
14741   GfxFrame[x][y] = -1;
14742 }
14743 #endif
14744
14745 /*
14746   =============================================================================
14747   checkDiagonalPushing()
14748   -----------------------------------------------------------------------------
14749   check if diagonal input device direction results in pushing of object
14750   (by checking if the alternative direction is walkable, diggable, ...)
14751   =============================================================================
14752 */
14753
14754 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14755                                     int x, int y, int real_dx, int real_dy)
14756 {
14757   int jx, jy, dx, dy, xx, yy;
14758
14759   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14760     return TRUE;
14761
14762   /* diagonal direction: check alternative direction */
14763   jx = player->jx;
14764   jy = player->jy;
14765   dx = x - jx;
14766   dy = y - jy;
14767   xx = jx + (dx == 0 ? real_dx : 0);
14768   yy = jy + (dy == 0 ? real_dy : 0);
14769
14770   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14771 }
14772
14773 /*
14774   =============================================================================
14775   DigField()
14776   -----------------------------------------------------------------------------
14777   x, y:                 field next to player (non-diagonal) to try to dig to
14778   real_dx, real_dy:     direction as read from input device (can be diagonal)
14779   =============================================================================
14780 */
14781
14782 static int DigField(struct PlayerInfo *player,
14783                     int oldx, int oldy, int x, int y,
14784                     int real_dx, int real_dy, int mode)
14785 {
14786   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14787   boolean player_was_pushing = player->is_pushing;
14788   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14789   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14790   int jx = oldx, jy = oldy;
14791   int dx = x - jx, dy = y - jy;
14792   int nextx = x + dx, nexty = y + dy;
14793   int move_direction = (dx == -1 ? MV_LEFT  :
14794                         dx == +1 ? MV_RIGHT :
14795                         dy == -1 ? MV_UP    :
14796                         dy == +1 ? MV_DOWN  : MV_NONE);
14797   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14798   int dig_side = MV_DIR_OPPOSITE(move_direction);
14799   int old_element = Feld[jx][jy];
14800 #if USE_FIXED_DONT_RUN_INTO
14801   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14802 #else
14803   int element;
14804 #endif
14805   int collect_count;
14806
14807   if (is_player)                /* function can also be called by EL_PENGUIN */
14808   {
14809     if (player->MovPos == 0)
14810     {
14811       player->is_digging = FALSE;
14812       player->is_collecting = FALSE;
14813     }
14814
14815     if (player->MovPos == 0)    /* last pushing move finished */
14816       player->is_pushing = FALSE;
14817
14818     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14819     {
14820       player->is_switching = FALSE;
14821       player->push_delay = -1;
14822
14823       return MP_NO_ACTION;
14824     }
14825   }
14826
14827 #if !USE_FIXED_DONT_RUN_INTO
14828   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14829     return MP_NO_ACTION;
14830 #endif
14831
14832   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14833     old_element = Back[jx][jy];
14834
14835   /* in case of element dropped at player position, check background */
14836   else if (Back[jx][jy] != EL_EMPTY &&
14837            game.engine_version >= VERSION_IDENT(2,2,0,0))
14838     old_element = Back[jx][jy];
14839
14840   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14841     return MP_NO_ACTION;        /* field has no opening in this direction */
14842
14843   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14844     return MP_NO_ACTION;        /* field has no opening in this direction */
14845
14846 #if USE_FIXED_DONT_RUN_INTO
14847   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14848   {
14849     SplashAcid(x, y);
14850
14851     Feld[jx][jy] = player->artwork_element;
14852     InitMovingField(jx, jy, MV_DOWN);
14853     Store[jx][jy] = EL_ACID;
14854     ContinueMoving(jx, jy);
14855     BuryPlayer(player);
14856
14857     return MP_DONT_RUN_INTO;
14858   }
14859 #endif
14860
14861 #if USE_FIXED_DONT_RUN_INTO
14862   if (player_can_move && DONT_RUN_INTO(element))
14863   {
14864     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14865
14866     return MP_DONT_RUN_INTO;
14867   }
14868 #endif
14869
14870 #if USE_FIXED_DONT_RUN_INTO
14871   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14872     return MP_NO_ACTION;
14873 #endif
14874
14875 #if !USE_FIXED_DONT_RUN_INTO
14876   element = Feld[x][y];
14877 #endif
14878
14879   collect_count = element_info[element].collect_count_initial;
14880
14881   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14882     return MP_NO_ACTION;
14883
14884   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14885     player_can_move = player_can_move_or_snap;
14886
14887   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14888       game.engine_version >= VERSION_IDENT(2,2,0,0))
14889   {
14890     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14891                                player->index_bit, dig_side);
14892     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14893                                         player->index_bit, dig_side);
14894
14895     if (element == EL_DC_LANDMINE)
14896       Bang(x, y);
14897
14898     if (Feld[x][y] != element)          /* field changed by snapping */
14899       return MP_ACTION;
14900
14901     return MP_NO_ACTION;
14902   }
14903
14904 #if USE_PLAYER_GRAVITY
14905   if (player->gravity && is_player && !player->is_auto_moving &&
14906       canFallDown(player) && move_direction != MV_DOWN &&
14907       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14908     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14909 #else
14910   if (game.gravity && is_player && !player->is_auto_moving &&
14911       canFallDown(player) && move_direction != MV_DOWN &&
14912       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14913     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14914 #endif
14915
14916   if (player_can_move &&
14917       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14918   {
14919     int sound_element = SND_ELEMENT(element);
14920     int sound_action = ACTION_WALKING;
14921
14922     if (IS_RND_GATE(element))
14923     {
14924       if (!player->key[RND_GATE_NR(element)])
14925         return MP_NO_ACTION;
14926     }
14927     else if (IS_RND_GATE_GRAY(element))
14928     {
14929       if (!player->key[RND_GATE_GRAY_NR(element)])
14930         return MP_NO_ACTION;
14931     }
14932     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14933     {
14934       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14935         return MP_NO_ACTION;
14936     }
14937     else if (element == EL_EXIT_OPEN ||
14938              element == EL_EM_EXIT_OPEN ||
14939 #if 1
14940              element == EL_EM_EXIT_OPENING ||
14941 #endif
14942              element == EL_STEEL_EXIT_OPEN ||
14943              element == EL_EM_STEEL_EXIT_OPEN ||
14944 #if 1
14945              element == EL_EM_STEEL_EXIT_OPENING ||
14946 #endif
14947              element == EL_SP_EXIT_OPEN ||
14948              element == EL_SP_EXIT_OPENING)
14949     {
14950       sound_action = ACTION_PASSING;    /* player is passing exit */
14951     }
14952     else if (element == EL_EMPTY)
14953     {
14954       sound_action = ACTION_MOVING;             /* nothing to walk on */
14955     }
14956
14957     /* play sound from background or player, whatever is available */
14958     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14959       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14960     else
14961       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14962   }
14963   else if (player_can_move &&
14964            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14965   {
14966     if (!ACCESS_FROM(element, opposite_direction))
14967       return MP_NO_ACTION;      /* field not accessible from this direction */
14968
14969     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14970       return MP_NO_ACTION;
14971
14972     if (IS_EM_GATE(element))
14973     {
14974       if (!player->key[EM_GATE_NR(element)])
14975         return MP_NO_ACTION;
14976     }
14977     else if (IS_EM_GATE_GRAY(element))
14978     {
14979       if (!player->key[EM_GATE_GRAY_NR(element)])
14980         return MP_NO_ACTION;
14981     }
14982     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14983     {
14984       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14985         return MP_NO_ACTION;
14986     }
14987     else if (IS_EMC_GATE(element))
14988     {
14989       if (!player->key[EMC_GATE_NR(element)])
14990         return MP_NO_ACTION;
14991     }
14992     else if (IS_EMC_GATE_GRAY(element))
14993     {
14994       if (!player->key[EMC_GATE_GRAY_NR(element)])
14995         return MP_NO_ACTION;
14996     }
14997     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14998     {
14999       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15000         return MP_NO_ACTION;
15001     }
15002     else if (element == EL_DC_GATE_WHITE ||
15003              element == EL_DC_GATE_WHITE_GRAY ||
15004              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15005     {
15006       if (player->num_white_keys == 0)
15007         return MP_NO_ACTION;
15008
15009       player->num_white_keys--;
15010     }
15011     else if (IS_SP_PORT(element))
15012     {
15013       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15014           element == EL_SP_GRAVITY_PORT_RIGHT ||
15015           element == EL_SP_GRAVITY_PORT_UP ||
15016           element == EL_SP_GRAVITY_PORT_DOWN)
15017 #if USE_PLAYER_GRAVITY
15018         player->gravity = !player->gravity;
15019 #else
15020         game.gravity = !game.gravity;
15021 #endif
15022       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15023                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15024                element == EL_SP_GRAVITY_ON_PORT_UP ||
15025                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15026 #if USE_PLAYER_GRAVITY
15027         player->gravity = TRUE;
15028 #else
15029         game.gravity = TRUE;
15030 #endif
15031       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15032                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15033                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15034                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15035 #if USE_PLAYER_GRAVITY
15036         player->gravity = FALSE;
15037 #else
15038         game.gravity = FALSE;
15039 #endif
15040     }
15041
15042     /* automatically move to the next field with double speed */
15043     player->programmed_action = move_direction;
15044
15045     if (player->move_delay_reset_counter == 0)
15046     {
15047       player->move_delay_reset_counter = 2;     /* two double speed steps */
15048
15049       DOUBLE_PLAYER_SPEED(player);
15050     }
15051
15052     PlayLevelSoundAction(x, y, ACTION_PASSING);
15053   }
15054   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15055   {
15056     RemoveField(x, y);
15057
15058     if (mode != DF_SNAP)
15059     {
15060       GfxElement[x][y] = GFX_ELEMENT(element);
15061       player->is_digging = TRUE;
15062     }
15063
15064     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15065
15066     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15067                                         player->index_bit, dig_side);
15068
15069     if (mode == DF_SNAP)
15070     {
15071 #if USE_NEW_SNAP_DELAY
15072       if (level.block_snap_field)
15073         setFieldForSnapping(x, y, element, move_direction);
15074       else
15075         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15076 #else
15077       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15078 #endif
15079
15080       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15081                                           player->index_bit, dig_side);
15082     }
15083   }
15084   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15085   {
15086     RemoveField(x, y);
15087
15088     if (is_player && mode != DF_SNAP)
15089     {
15090       GfxElement[x][y] = element;
15091       player->is_collecting = TRUE;
15092     }
15093
15094     if (element == EL_SPEED_PILL)
15095     {
15096       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15097     }
15098     else if (element == EL_EXTRA_TIME && level.time > 0)
15099     {
15100       TimeLeft += level.extra_time;
15101
15102 #if 1
15103       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15104
15105       DisplayGameControlValues();
15106 #else
15107       DrawGameValue_Time(TimeLeft);
15108 #endif
15109     }
15110     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15111     {
15112       player->shield_normal_time_left += level.shield_normal_time;
15113       if (element == EL_SHIELD_DEADLY)
15114         player->shield_deadly_time_left += level.shield_deadly_time;
15115     }
15116     else if (element == EL_DYNAMITE ||
15117              element == EL_EM_DYNAMITE ||
15118              element == EL_SP_DISK_RED)
15119     {
15120       if (player->inventory_size < MAX_INVENTORY_SIZE)
15121         player->inventory_element[player->inventory_size++] = element;
15122
15123       DrawGameDoorValues();
15124     }
15125     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15126     {
15127       player->dynabomb_count++;
15128       player->dynabombs_left++;
15129     }
15130     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15131     {
15132       player->dynabomb_size++;
15133     }
15134     else if (element == EL_DYNABOMB_INCREASE_POWER)
15135     {
15136       player->dynabomb_xl = TRUE;
15137     }
15138     else if (IS_KEY(element))
15139     {
15140       player->key[KEY_NR(element)] = TRUE;
15141
15142       DrawGameDoorValues();
15143     }
15144     else if (element == EL_DC_KEY_WHITE)
15145     {
15146       player->num_white_keys++;
15147
15148       /* display white keys? */
15149       /* DrawGameDoorValues(); */
15150     }
15151     else if (IS_ENVELOPE(element))
15152     {
15153       player->show_envelope = element;
15154     }
15155     else if (element == EL_EMC_LENSES)
15156     {
15157       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15158
15159       RedrawAllInvisibleElementsForLenses();
15160     }
15161     else if (element == EL_EMC_MAGNIFIER)
15162     {
15163       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15164
15165       RedrawAllInvisibleElementsForMagnifier();
15166     }
15167     else if (IS_DROPPABLE(element) ||
15168              IS_THROWABLE(element))     /* can be collected and dropped */
15169     {
15170       int i;
15171
15172       if (collect_count == 0)
15173         player->inventory_infinite_element = element;
15174       else
15175         for (i = 0; i < collect_count; i++)
15176           if (player->inventory_size < MAX_INVENTORY_SIZE)
15177             player->inventory_element[player->inventory_size++] = element;
15178
15179       DrawGameDoorValues();
15180     }
15181     else if (collect_count > 0)
15182     {
15183       local_player->gems_still_needed -= collect_count;
15184       if (local_player->gems_still_needed < 0)
15185         local_player->gems_still_needed = 0;
15186
15187 #if 1
15188       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15189
15190       DisplayGameControlValues();
15191 #else
15192       DrawGameValue_Emeralds(local_player->gems_still_needed);
15193 #endif
15194     }
15195
15196     RaiseScoreElement(element);
15197     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15198
15199     if (is_player)
15200       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15201                                           player->index_bit, dig_side);
15202
15203     if (mode == DF_SNAP)
15204     {
15205 #if USE_NEW_SNAP_DELAY
15206       if (level.block_snap_field)
15207         setFieldForSnapping(x, y, element, move_direction);
15208       else
15209         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15210 #else
15211       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15212 #endif
15213
15214       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15215                                           player->index_bit, dig_side);
15216     }
15217   }
15218   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15219   {
15220     if (mode == DF_SNAP && element != EL_BD_ROCK)
15221       return MP_NO_ACTION;
15222
15223     if (CAN_FALL(element) && dy)
15224       return MP_NO_ACTION;
15225
15226     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15227         !(element == EL_SPRING && level.use_spring_bug))
15228       return MP_NO_ACTION;
15229
15230     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15231         ((move_direction & MV_VERTICAL &&
15232           ((element_info[element].move_pattern & MV_LEFT &&
15233             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15234            (element_info[element].move_pattern & MV_RIGHT &&
15235             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15236          (move_direction & MV_HORIZONTAL &&
15237           ((element_info[element].move_pattern & MV_UP &&
15238             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15239            (element_info[element].move_pattern & MV_DOWN &&
15240             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15241       return MP_NO_ACTION;
15242
15243     /* do not push elements already moving away faster than player */
15244     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15245         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15246       return MP_NO_ACTION;
15247
15248     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15249     {
15250       if (player->push_delay_value == -1 || !player_was_pushing)
15251         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15252     }
15253     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15254     {
15255       if (player->push_delay_value == -1)
15256         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15257     }
15258     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15259     {
15260       if (!player->is_pushing)
15261         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15262     }
15263
15264     player->is_pushing = TRUE;
15265     player->is_active = TRUE;
15266
15267     if (!(IN_LEV_FIELD(nextx, nexty) &&
15268           (IS_FREE(nextx, nexty) ||
15269            (IS_SB_ELEMENT(element) &&
15270             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15271            (IS_CUSTOM_ELEMENT(element) &&
15272             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15273       return MP_NO_ACTION;
15274
15275     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15276       return MP_NO_ACTION;
15277
15278     if (player->push_delay == -1)       /* new pushing; restart delay */
15279       player->push_delay = 0;
15280
15281     if (player->push_delay < player->push_delay_value &&
15282         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15283         element != EL_SPRING && element != EL_BALLOON)
15284     {
15285       /* make sure that there is no move delay before next try to push */
15286       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15287         player->move_delay = 0;
15288
15289       return MP_NO_ACTION;
15290     }
15291
15292     if (IS_CUSTOM_ELEMENT(element) &&
15293         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15294     {
15295       if (!DigFieldByCE(nextx, nexty, element))
15296         return MP_NO_ACTION;
15297     }
15298
15299     if (IS_SB_ELEMENT(element))
15300     {
15301       if (element == EL_SOKOBAN_FIELD_FULL)
15302       {
15303         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15304         local_player->sokobanfields_still_needed++;
15305       }
15306
15307       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15308       {
15309         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15310         local_player->sokobanfields_still_needed--;
15311       }
15312
15313       Feld[x][y] = EL_SOKOBAN_OBJECT;
15314
15315       if (Back[x][y] == Back[nextx][nexty])
15316         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15317       else if (Back[x][y] != 0)
15318         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15319                                     ACTION_EMPTYING);
15320       else
15321         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15322                                     ACTION_FILLING);
15323
15324       if (local_player->sokobanfields_still_needed == 0 &&
15325           game.emulation == EMU_SOKOBAN)
15326       {
15327         PlayerWins(player);
15328
15329         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15330       }
15331     }
15332     else
15333       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15334
15335     InitMovingField(x, y, move_direction);
15336     GfxAction[x][y] = ACTION_PUSHING;
15337
15338     if (mode == DF_SNAP)
15339       ContinueMoving(x, y);
15340     else
15341       MovPos[x][y] = (dx != 0 ? dx : dy);
15342
15343     Pushed[x][y] = TRUE;
15344     Pushed[nextx][nexty] = TRUE;
15345
15346     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15347       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15348     else
15349       player->push_delay_value = -1;    /* get new value later */
15350
15351     /* check for element change _after_ element has been pushed */
15352     if (game.use_change_when_pushing_bug)
15353     {
15354       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15355                                  player->index_bit, dig_side);
15356       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15357                                           player->index_bit, dig_side);
15358     }
15359   }
15360   else if (IS_SWITCHABLE(element))
15361   {
15362     if (PLAYER_SWITCHING(player, x, y))
15363     {
15364       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15365                                           player->index_bit, dig_side);
15366
15367       return MP_ACTION;
15368     }
15369
15370     player->is_switching = TRUE;
15371     player->switch_x = x;
15372     player->switch_y = y;
15373
15374     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15375
15376     if (element == EL_ROBOT_WHEEL)
15377     {
15378       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15379       ZX = x;
15380       ZY = y;
15381
15382       game.robot_wheel_active = TRUE;
15383
15384       TEST_DrawLevelField(x, y);
15385     }
15386     else if (element == EL_SP_TERMINAL)
15387     {
15388       int xx, yy;
15389
15390       SCAN_PLAYFIELD(xx, yy)
15391       {
15392         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15393           Bang(xx, yy);
15394         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15395           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15396       }
15397     }
15398     else if (IS_BELT_SWITCH(element))
15399     {
15400       ToggleBeltSwitch(x, y);
15401     }
15402     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15403              element == EL_SWITCHGATE_SWITCH_DOWN ||
15404              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15405              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15406     {
15407       ToggleSwitchgateSwitch(x, y);
15408     }
15409     else if (element == EL_LIGHT_SWITCH ||
15410              element == EL_LIGHT_SWITCH_ACTIVE)
15411     {
15412       ToggleLightSwitch(x, y);
15413     }
15414     else if (element == EL_TIMEGATE_SWITCH ||
15415              element == EL_DC_TIMEGATE_SWITCH)
15416     {
15417       ActivateTimegateSwitch(x, y);
15418     }
15419     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15420              element == EL_BALLOON_SWITCH_RIGHT ||
15421              element == EL_BALLOON_SWITCH_UP    ||
15422              element == EL_BALLOON_SWITCH_DOWN  ||
15423              element == EL_BALLOON_SWITCH_NONE  ||
15424              element == EL_BALLOON_SWITCH_ANY)
15425     {
15426       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15427                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15428                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15429                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15430                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15431                              move_direction);
15432     }
15433     else if (element == EL_LAMP)
15434     {
15435       Feld[x][y] = EL_LAMP_ACTIVE;
15436       local_player->lights_still_needed--;
15437
15438       ResetGfxAnimation(x, y);
15439       TEST_DrawLevelField(x, y);
15440     }
15441     else if (element == EL_TIME_ORB_FULL)
15442     {
15443       Feld[x][y] = EL_TIME_ORB_EMPTY;
15444
15445       if (level.time > 0 || level.use_time_orb_bug)
15446       {
15447         TimeLeft += level.time_orb_time;
15448
15449 #if 1
15450         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15451
15452         DisplayGameControlValues();
15453 #else
15454         DrawGameValue_Time(TimeLeft);
15455 #endif
15456       }
15457
15458       ResetGfxAnimation(x, y);
15459       TEST_DrawLevelField(x, y);
15460     }
15461     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15462              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15463     {
15464       int xx, yy;
15465
15466       game.ball_state = !game.ball_state;
15467
15468       SCAN_PLAYFIELD(xx, yy)
15469       {
15470         int e = Feld[xx][yy];
15471
15472         if (game.ball_state)
15473         {
15474           if (e == EL_EMC_MAGIC_BALL)
15475             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15476           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15477             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15478         }
15479         else
15480         {
15481           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15482             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15483           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15484             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15485         }
15486       }
15487     }
15488
15489     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15490                                         player->index_bit, dig_side);
15491
15492     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15493                                         player->index_bit, dig_side);
15494
15495     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15496                                         player->index_bit, dig_side);
15497
15498     return MP_ACTION;
15499   }
15500   else
15501   {
15502     if (!PLAYER_SWITCHING(player, x, y))
15503     {
15504       player->is_switching = TRUE;
15505       player->switch_x = x;
15506       player->switch_y = y;
15507
15508       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15509                                  player->index_bit, dig_side);
15510       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15511                                           player->index_bit, dig_side);
15512
15513       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15514                                  player->index_bit, dig_side);
15515       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15516                                           player->index_bit, dig_side);
15517     }
15518
15519     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15520                                player->index_bit, dig_side);
15521     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15522                                         player->index_bit, dig_side);
15523
15524     return MP_NO_ACTION;
15525   }
15526
15527   player->push_delay = -1;
15528
15529   if (is_player)                /* function can also be called by EL_PENGUIN */
15530   {
15531     if (Feld[x][y] != element)          /* really digged/collected something */
15532     {
15533       player->is_collecting = !player->is_digging;
15534       player->is_active = TRUE;
15535     }
15536   }
15537
15538   return MP_MOVING;
15539 }
15540
15541 static boolean DigFieldByCE(int x, int y, int digging_element)
15542 {
15543   int element = Feld[x][y];
15544
15545   if (!IS_FREE(x, y))
15546   {
15547     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15548                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15549                   ACTION_BREAKING);
15550
15551     /* no element can dig solid indestructible elements */
15552     if (IS_INDESTRUCTIBLE(element) &&
15553         !IS_DIGGABLE(element) &&
15554         !IS_COLLECTIBLE(element))
15555       return FALSE;
15556
15557     if (AmoebaNr[x][y] &&
15558         (element == EL_AMOEBA_FULL ||
15559          element == EL_BD_AMOEBA ||
15560          element == EL_AMOEBA_GROWING))
15561     {
15562       AmoebaCnt[AmoebaNr[x][y]]--;
15563       AmoebaCnt2[AmoebaNr[x][y]]--;
15564     }
15565
15566     if (IS_MOVING(x, y))
15567       RemoveMovingField(x, y);
15568     else
15569     {
15570       RemoveField(x, y);
15571       TEST_DrawLevelField(x, y);
15572     }
15573
15574     /* if digged element was about to explode, prevent the explosion */
15575     ExplodeField[x][y] = EX_TYPE_NONE;
15576
15577     PlayLevelSoundAction(x, y, action);
15578   }
15579
15580   Store[x][y] = EL_EMPTY;
15581
15582 #if 1
15583   /* this makes it possible to leave the removed element again */
15584   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15585     Store[x][y] = element;
15586 #else
15587   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15588   {
15589     int move_leave_element = element_info[digging_element].move_leave_element;
15590
15591     /* this makes it possible to leave the removed element again */
15592     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15593                    element : move_leave_element);
15594   }
15595 #endif
15596
15597   return TRUE;
15598 }
15599
15600 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15601 {
15602   int jx = player->jx, jy = player->jy;
15603   int x = jx + dx, y = jy + dy;
15604   int snap_direction = (dx == -1 ? MV_LEFT  :
15605                         dx == +1 ? MV_RIGHT :
15606                         dy == -1 ? MV_UP    :
15607                         dy == +1 ? MV_DOWN  : MV_NONE);
15608   boolean can_continue_snapping = (level.continuous_snapping &&
15609                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15610
15611   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15612     return FALSE;
15613
15614   if (!player->active || !IN_LEV_FIELD(x, y))
15615     return FALSE;
15616
15617   if (dx && dy)
15618     return FALSE;
15619
15620   if (!dx && !dy)
15621   {
15622     if (player->MovPos == 0)
15623       player->is_pushing = FALSE;
15624
15625     player->is_snapping = FALSE;
15626
15627     if (player->MovPos == 0)
15628     {
15629       player->is_moving = FALSE;
15630       player->is_digging = FALSE;
15631       player->is_collecting = FALSE;
15632     }
15633
15634     return FALSE;
15635   }
15636
15637 #if USE_NEW_CONTINUOUS_SNAPPING
15638   /* prevent snapping with already pressed snap key when not allowed */
15639   if (player->is_snapping && !can_continue_snapping)
15640     return FALSE;
15641 #else
15642   if (player->is_snapping)
15643     return FALSE;
15644 #endif
15645
15646   player->MovDir = snap_direction;
15647
15648   if (player->MovPos == 0)
15649   {
15650     player->is_moving = FALSE;
15651     player->is_digging = FALSE;
15652     player->is_collecting = FALSE;
15653   }
15654
15655   player->is_dropping = FALSE;
15656   player->is_dropping_pressed = FALSE;
15657   player->drop_pressed_delay = 0;
15658
15659   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15660     return FALSE;
15661
15662   player->is_snapping = TRUE;
15663   player->is_active = TRUE;
15664
15665   if (player->MovPos == 0)
15666   {
15667     player->is_moving = FALSE;
15668     player->is_digging = FALSE;
15669     player->is_collecting = FALSE;
15670   }
15671
15672   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15673     TEST_DrawLevelField(player->last_jx, player->last_jy);
15674
15675   TEST_DrawLevelField(x, y);
15676
15677   return TRUE;
15678 }
15679
15680 static boolean DropElement(struct PlayerInfo *player)
15681 {
15682   int old_element, new_element;
15683   int dropx = player->jx, dropy = player->jy;
15684   int drop_direction = player->MovDir;
15685   int drop_side = drop_direction;
15686 #if 1
15687   int drop_element = get_next_dropped_element(player);
15688 #else
15689   int drop_element = (player->inventory_size > 0 ?
15690                       player->inventory_element[player->inventory_size - 1] :
15691                       player->inventory_infinite_element != EL_UNDEFINED ?
15692                       player->inventory_infinite_element :
15693                       player->dynabombs_left > 0 ?
15694                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15695                       EL_UNDEFINED);
15696 #endif
15697
15698   player->is_dropping_pressed = TRUE;
15699
15700   /* do not drop an element on top of another element; when holding drop key
15701      pressed without moving, dropped element must move away before the next
15702      element can be dropped (this is especially important if the next element
15703      is dynamite, which can be placed on background for historical reasons) */
15704   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15705     return MP_ACTION;
15706
15707   if (IS_THROWABLE(drop_element))
15708   {
15709     dropx += GET_DX_FROM_DIR(drop_direction);
15710     dropy += GET_DY_FROM_DIR(drop_direction);
15711
15712     if (!IN_LEV_FIELD(dropx, dropy))
15713       return FALSE;
15714   }
15715
15716   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15717   new_element = drop_element;           /* default: no change when dropping */
15718
15719   /* check if player is active, not moving and ready to drop */
15720   if (!player->active || player->MovPos || player->drop_delay > 0)
15721     return FALSE;
15722
15723   /* check if player has anything that can be dropped */
15724   if (new_element == EL_UNDEFINED)
15725     return FALSE;
15726
15727   /* check if drop key was pressed long enough for EM style dynamite */
15728   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15729     return FALSE;
15730
15731   /* check if anything can be dropped at the current position */
15732   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15733     return FALSE;
15734
15735   /* collected custom elements can only be dropped on empty fields */
15736   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15737     return FALSE;
15738
15739   if (old_element != EL_EMPTY)
15740     Back[dropx][dropy] = old_element;   /* store old element on this field */
15741
15742   ResetGfxAnimation(dropx, dropy);
15743   ResetRandomAnimationValue(dropx, dropy);
15744
15745   if (player->inventory_size > 0 ||
15746       player->inventory_infinite_element != EL_UNDEFINED)
15747   {
15748     if (player->inventory_size > 0)
15749     {
15750       player->inventory_size--;
15751
15752       DrawGameDoorValues();
15753
15754       if (new_element == EL_DYNAMITE)
15755         new_element = EL_DYNAMITE_ACTIVE;
15756       else if (new_element == EL_EM_DYNAMITE)
15757         new_element = EL_EM_DYNAMITE_ACTIVE;
15758       else if (new_element == EL_SP_DISK_RED)
15759         new_element = EL_SP_DISK_RED_ACTIVE;
15760     }
15761
15762     Feld[dropx][dropy] = new_element;
15763
15764     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15765       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15766                           el2img(Feld[dropx][dropy]), 0);
15767
15768     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15769
15770     /* needed if previous element just changed to "empty" in the last frame */
15771     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15772
15773     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15774                                player->index_bit, drop_side);
15775     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15776                                         CE_PLAYER_DROPS_X,
15777                                         player->index_bit, drop_side);
15778
15779     TestIfElementTouchesCustomElement(dropx, dropy);
15780   }
15781   else          /* player is dropping a dyna bomb */
15782   {
15783     player->dynabombs_left--;
15784
15785     Feld[dropx][dropy] = new_element;
15786
15787     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15788       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15789                           el2img(Feld[dropx][dropy]), 0);
15790
15791     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15792   }
15793
15794   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15795     InitField_WithBug1(dropx, dropy, FALSE);
15796
15797   new_element = Feld[dropx][dropy];     /* element might have changed */
15798
15799   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15800       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15801   {
15802     int move_direction, nextx, nexty;
15803
15804     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15805       MovDir[dropx][dropy] = drop_direction;
15806
15807     move_direction = MovDir[dropx][dropy];
15808     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15809     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15810
15811     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15812
15813 #if USE_FIX_IMPACT_COLLISION
15814     /* do not cause impact style collision by dropping elements that can fall */
15815     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15816 #else
15817     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15818 #endif
15819   }
15820
15821   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15822   player->is_dropping = TRUE;
15823
15824   player->drop_pressed_delay = 0;
15825   player->is_dropping_pressed = FALSE;
15826
15827   player->drop_x = dropx;
15828   player->drop_y = dropy;
15829
15830   return TRUE;
15831 }
15832
15833 /* ------------------------------------------------------------------------- */
15834 /* game sound playing functions                                              */
15835 /* ------------------------------------------------------------------------- */
15836
15837 static int *loop_sound_frame = NULL;
15838 static int *loop_sound_volume = NULL;
15839
15840 void InitPlayLevelSound()
15841 {
15842   int num_sounds = getSoundListSize();
15843
15844   checked_free(loop_sound_frame);
15845   checked_free(loop_sound_volume);
15846
15847   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15848   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15849 }
15850
15851 static void PlayLevelSound(int x, int y, int nr)
15852 {
15853   int sx = SCREENX(x), sy = SCREENY(y);
15854   int volume, stereo_position;
15855   int max_distance = 8;
15856   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15857
15858   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15859       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15860     return;
15861
15862   if (!IN_LEV_FIELD(x, y) ||
15863       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15864       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15865     return;
15866
15867   volume = SOUND_MAX_VOLUME;
15868
15869   if (!IN_SCR_FIELD(sx, sy))
15870   {
15871     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15872     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15873
15874     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15875   }
15876
15877   stereo_position = (SOUND_MAX_LEFT +
15878                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15879                      (SCR_FIELDX + 2 * max_distance));
15880
15881   if (IS_LOOP_SOUND(nr))
15882   {
15883     /* This assures that quieter loop sounds do not overwrite louder ones,
15884        while restarting sound volume comparison with each new game frame. */
15885
15886     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15887       return;
15888
15889     loop_sound_volume[nr] = volume;
15890     loop_sound_frame[nr] = FrameCounter;
15891   }
15892
15893   PlaySoundExt(nr, volume, stereo_position, type);
15894 }
15895
15896 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15897 {
15898   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15899                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15900                  y < LEVELY(BY1) ? LEVELY(BY1) :
15901                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15902                  sound_action);
15903 }
15904
15905 static void PlayLevelSoundAction(int x, int y, int action)
15906 {
15907   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15908 }
15909
15910 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15911 {
15912   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15913
15914   if (sound_effect != SND_UNDEFINED)
15915     PlayLevelSound(x, y, sound_effect);
15916 }
15917
15918 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15919                                               int action)
15920 {
15921   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15922
15923   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15924     PlayLevelSound(x, y, sound_effect);
15925 }
15926
15927 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15928 {
15929   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15930
15931   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932     PlayLevelSound(x, y, sound_effect);
15933 }
15934
15935 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15936 {
15937   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15938
15939   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940     StopSound(sound_effect);
15941 }
15942
15943 static void PlayLevelMusic()
15944 {
15945   if (levelset.music[level_nr] != MUS_UNDEFINED)
15946     PlayMusic(levelset.music[level_nr]);        /* from config file */
15947   else
15948     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15949 }
15950
15951 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15952 {
15953   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15954   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15955   int x = xx - 1 - offset;
15956   int y = yy - 1 - offset;
15957
15958   switch (sample)
15959   {
15960     case SAMPLE_blank:
15961       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15962       break;
15963
15964     case SAMPLE_roll:
15965       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15966       break;
15967
15968     case SAMPLE_stone:
15969       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15970       break;
15971
15972     case SAMPLE_nut:
15973       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15974       break;
15975
15976     case SAMPLE_crack:
15977       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15978       break;
15979
15980     case SAMPLE_bug:
15981       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15982       break;
15983
15984     case SAMPLE_tank:
15985       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15986       break;
15987
15988     case SAMPLE_android_clone:
15989       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15990       break;
15991
15992     case SAMPLE_android_move:
15993       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15994       break;
15995
15996     case SAMPLE_spring:
15997       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15998       break;
15999
16000     case SAMPLE_slurp:
16001       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16002       break;
16003
16004     case SAMPLE_eater:
16005       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16006       break;
16007
16008     case SAMPLE_eater_eat:
16009       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16010       break;
16011
16012     case SAMPLE_alien:
16013       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16014       break;
16015
16016     case SAMPLE_collect:
16017       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16018       break;
16019
16020     case SAMPLE_diamond:
16021       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16022       break;
16023
16024     case SAMPLE_squash:
16025       /* !!! CHECK THIS !!! */
16026 #if 1
16027       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16028 #else
16029       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16030 #endif
16031       break;
16032
16033     case SAMPLE_wonderfall:
16034       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16035       break;
16036
16037     case SAMPLE_drip:
16038       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16039       break;
16040
16041     case SAMPLE_push:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16043       break;
16044
16045     case SAMPLE_dirt:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16047       break;
16048
16049     case SAMPLE_acid:
16050       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16051       break;
16052
16053     case SAMPLE_ball:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16055       break;
16056
16057     case SAMPLE_grow:
16058       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16059       break;
16060
16061     case SAMPLE_wonder:
16062       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16063       break;
16064
16065     case SAMPLE_door:
16066       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16067       break;
16068
16069     case SAMPLE_exit_open:
16070       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16071       break;
16072
16073     case SAMPLE_exit_leave:
16074       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16075       break;
16076
16077     case SAMPLE_dynamite:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16079       break;
16080
16081     case SAMPLE_tick:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16083       break;
16084
16085     case SAMPLE_press:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16087       break;
16088
16089     case SAMPLE_wheel:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16091       break;
16092
16093     case SAMPLE_boom:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16095       break;
16096
16097     case SAMPLE_die:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16099       break;
16100
16101     case SAMPLE_time:
16102       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16103       break;
16104
16105     default:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16107       break;
16108   }
16109 }
16110
16111 #if 0
16112 void ChangeTime(int value)
16113 {
16114   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16115
16116   *time += value;
16117
16118   /* EMC game engine uses value from time counter of RND game engine */
16119   level.native_em_level->lev->time = *time;
16120
16121   DrawGameValue_Time(*time);
16122 }
16123
16124 void RaiseScore(int value)
16125 {
16126   /* EMC game engine and RND game engine have separate score counters */
16127   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16128                 &level.native_em_level->lev->score : &local_player->score);
16129
16130   *score += value;
16131
16132   DrawGameValue_Score(*score);
16133 }
16134 #endif
16135
16136 void RaiseScore(int value)
16137 {
16138   local_player->score += value;
16139
16140 #if 1
16141   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16142
16143   DisplayGameControlValues();
16144 #else
16145   DrawGameValue_Score(local_player->score);
16146 #endif
16147 }
16148
16149 void RaiseScoreElement(int element)
16150 {
16151   switch (element)
16152   {
16153     case EL_EMERALD:
16154     case EL_BD_DIAMOND:
16155     case EL_EMERALD_YELLOW:
16156     case EL_EMERALD_RED:
16157     case EL_EMERALD_PURPLE:
16158     case EL_SP_INFOTRON:
16159       RaiseScore(level.score[SC_EMERALD]);
16160       break;
16161     case EL_DIAMOND:
16162       RaiseScore(level.score[SC_DIAMOND]);
16163       break;
16164     case EL_CRYSTAL:
16165       RaiseScore(level.score[SC_CRYSTAL]);
16166       break;
16167     case EL_PEARL:
16168       RaiseScore(level.score[SC_PEARL]);
16169       break;
16170     case EL_BUG:
16171     case EL_BD_BUTTERFLY:
16172     case EL_SP_ELECTRON:
16173       RaiseScore(level.score[SC_BUG]);
16174       break;
16175     case EL_SPACESHIP:
16176     case EL_BD_FIREFLY:
16177     case EL_SP_SNIKSNAK:
16178       RaiseScore(level.score[SC_SPACESHIP]);
16179       break;
16180     case EL_YAMYAM:
16181     case EL_DARK_YAMYAM:
16182       RaiseScore(level.score[SC_YAMYAM]);
16183       break;
16184     case EL_ROBOT:
16185       RaiseScore(level.score[SC_ROBOT]);
16186       break;
16187     case EL_PACMAN:
16188       RaiseScore(level.score[SC_PACMAN]);
16189       break;
16190     case EL_NUT:
16191       RaiseScore(level.score[SC_NUT]);
16192       break;
16193     case EL_DYNAMITE:
16194     case EL_EM_DYNAMITE:
16195     case EL_SP_DISK_RED:
16196     case EL_DYNABOMB_INCREASE_NUMBER:
16197     case EL_DYNABOMB_INCREASE_SIZE:
16198     case EL_DYNABOMB_INCREASE_POWER:
16199       RaiseScore(level.score[SC_DYNAMITE]);
16200       break;
16201     case EL_SHIELD_NORMAL:
16202     case EL_SHIELD_DEADLY:
16203       RaiseScore(level.score[SC_SHIELD]);
16204       break;
16205     case EL_EXTRA_TIME:
16206       RaiseScore(level.extra_time_score);
16207       break;
16208     case EL_KEY_1:
16209     case EL_KEY_2:
16210     case EL_KEY_3:
16211     case EL_KEY_4:
16212     case EL_EM_KEY_1:
16213     case EL_EM_KEY_2:
16214     case EL_EM_KEY_3:
16215     case EL_EM_KEY_4:
16216     case EL_EMC_KEY_5:
16217     case EL_EMC_KEY_6:
16218     case EL_EMC_KEY_7:
16219     case EL_EMC_KEY_8:
16220     case EL_DC_KEY_WHITE:
16221       RaiseScore(level.score[SC_KEY]);
16222       break;
16223     default:
16224       RaiseScore(element_info[element].collect_score);
16225       break;
16226   }
16227 }
16228
16229 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16230 {
16231   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16232   {
16233 #if defined(NETWORK_AVALIABLE)
16234     if (options.network)
16235       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16236     else
16237 #endif
16238     {
16239       if (quick_quit)
16240       {
16241 #if 1
16242
16243 #if 1
16244         FadeSkipNextFadeIn();
16245 #else
16246         fading = fading_none;
16247 #endif
16248
16249 #else
16250         OpenDoor(DOOR_CLOSE_1);
16251 #endif
16252
16253         game_status = GAME_MODE_MAIN;
16254
16255 #if 1
16256         DrawAndFadeInMainMenu(REDRAW_FIELD);
16257 #else
16258         DrawMainMenu();
16259 #endif
16260       }
16261       else
16262       {
16263 #if 0
16264         FadeOut(REDRAW_FIELD);
16265 #endif
16266
16267         game_status = GAME_MODE_MAIN;
16268
16269         DrawAndFadeInMainMenu(REDRAW_FIELD);
16270       }
16271     }
16272   }
16273   else          /* continue playing the game */
16274   {
16275     if (tape.playing && tape.deactivate_display)
16276       TapeDeactivateDisplayOff(TRUE);
16277
16278     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16279
16280     if (tape.playing && tape.deactivate_display)
16281       TapeDeactivateDisplayOn();
16282   }
16283 }
16284
16285 void RequestQuitGame(boolean ask_if_really_quit)
16286 {
16287   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16288   boolean skip_request = AllPlayersGone || quick_quit;
16289
16290   RequestQuitGameExt(skip_request, quick_quit,
16291                      "Do you really want to quit the game ?");
16292 }
16293
16294
16295 /* ------------------------------------------------------------------------- */
16296 /* random generator functions                                                */
16297 /* ------------------------------------------------------------------------- */
16298
16299 unsigned int InitEngineRandom_RND(long seed)
16300 {
16301   game.num_random_calls = 0;
16302
16303 #if 0
16304   unsigned int rnd_seed = InitEngineRandom(seed);
16305
16306   printf("::: START RND: %d\n", rnd_seed);
16307
16308   return rnd_seed;
16309 #else
16310
16311   return InitEngineRandom(seed);
16312
16313 #endif
16314
16315 }
16316
16317 unsigned int RND(int max)
16318 {
16319   if (max > 0)
16320   {
16321     game.num_random_calls++;
16322
16323     return GetEngineRandom(max);
16324   }
16325
16326   return 0;
16327 }
16328
16329
16330 /* ------------------------------------------------------------------------- */
16331 /* game engine snapshot handling functions                                   */
16332 /* ------------------------------------------------------------------------- */
16333
16334 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16335
16336 struct EngineSnapshotInfo
16337 {
16338   /* runtime values for custom element collect score */
16339   int collect_score[NUM_CUSTOM_ELEMENTS];
16340
16341   /* runtime values for group element choice position */
16342   int choice_pos[NUM_GROUP_ELEMENTS];
16343
16344   /* runtime values for belt position animations */
16345   int belt_graphic[4 * NUM_BELT_PARTS];
16346   int belt_anim_mode[4 * NUM_BELT_PARTS];
16347 };
16348
16349 struct EngineSnapshotNodeInfo
16350 {
16351   void *buffer_orig;
16352   void *buffer_copy;
16353   int size;
16354 };
16355
16356 static struct EngineSnapshotInfo engine_snapshot_rnd;
16357 static ListNode *engine_snapshot_list = NULL;
16358 static char *snapshot_level_identifier = NULL;
16359 static int snapshot_level_nr = -1;
16360
16361 void FreeEngineSnapshot()
16362 {
16363   while (engine_snapshot_list != NULL)
16364     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16365                        checked_free);
16366
16367   setString(&snapshot_level_identifier, NULL);
16368   snapshot_level_nr = -1;
16369 }
16370
16371 static void SaveEngineSnapshotValues_RND()
16372 {
16373   static int belt_base_active_element[4] =
16374   {
16375     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16376     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16377     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16378     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16379   };
16380   int i, j;
16381
16382   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16383   {
16384     int element = EL_CUSTOM_START + i;
16385
16386     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16387   }
16388
16389   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16390   {
16391     int element = EL_GROUP_START + i;
16392
16393     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16394   }
16395
16396   for (i = 0; i < 4; i++)
16397   {
16398     for (j = 0; j < NUM_BELT_PARTS; j++)
16399     {
16400       int element = belt_base_active_element[i] + j;
16401       int graphic = el2img(element);
16402       int anim_mode = graphic_info[graphic].anim_mode;
16403
16404       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16405       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16406     }
16407   }
16408 }
16409
16410 static void LoadEngineSnapshotValues_RND()
16411 {
16412   unsigned long num_random_calls = game.num_random_calls;
16413   int i, j;
16414
16415   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16416   {
16417     int element = EL_CUSTOM_START + i;
16418
16419     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16420   }
16421
16422   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16423   {
16424     int element = EL_GROUP_START + i;
16425
16426     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16427   }
16428
16429   for (i = 0; i < 4; i++)
16430   {
16431     for (j = 0; j < NUM_BELT_PARTS; j++)
16432     {
16433       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16434       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16435
16436       graphic_info[graphic].anim_mode = anim_mode;
16437     }
16438   }
16439
16440   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16441   {
16442     InitRND(tape.random_seed);
16443     for (i = 0; i < num_random_calls; i++)
16444       RND(1);
16445   }
16446
16447   if (game.num_random_calls != num_random_calls)
16448   {
16449     Error(ERR_INFO, "number of random calls out of sync");
16450     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16451     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16452     Error(ERR_EXIT, "this should not happen -- please debug");
16453   }
16454 }
16455
16456 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16457 {
16458   struct EngineSnapshotNodeInfo *bi =
16459     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16460
16461   bi->buffer_orig = buffer;
16462   bi->buffer_copy = checked_malloc(size);
16463   bi->size = size;
16464
16465   memcpy(bi->buffer_copy, buffer, size);
16466
16467   addNodeToList(&engine_snapshot_list, NULL, bi);
16468 }
16469
16470 void SaveEngineSnapshot()
16471 {
16472   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16473
16474   if (level_editor_test_game)   /* do not save snapshots from editor */
16475     return;
16476
16477   /* copy some special values to a structure better suited for the snapshot */
16478
16479   SaveEngineSnapshotValues_RND();
16480   SaveEngineSnapshotValues_EM();
16481
16482   /* save values stored in special snapshot structure */
16483
16484   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16485   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16486
16487   /* save further RND engine values */
16488
16489   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16490   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16491   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16492
16493   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16494   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16495   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16496   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16497
16498   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16499   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16500   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16501   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16502   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16503
16504   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16505   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16506   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16507
16508   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16509
16510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16511
16512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16513   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16514
16515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16517   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16522   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16533
16534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16536
16537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16540
16541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16543
16544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16549
16550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16552
16553   /* save level identification information */
16554
16555   setString(&snapshot_level_identifier, leveldir_current->identifier);
16556   snapshot_level_nr = level_nr;
16557
16558 #if 0
16559   ListNode *node = engine_snapshot_list;
16560   int num_bytes = 0;
16561
16562   while (node != NULL)
16563   {
16564     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16565
16566     node = node->next;
16567   }
16568
16569   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16570 #endif
16571 }
16572
16573 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16574 {
16575   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16576 }
16577
16578 void LoadEngineSnapshot()
16579 {
16580   ListNode *node = engine_snapshot_list;
16581
16582   if (engine_snapshot_list == NULL)
16583     return;
16584
16585   while (node != NULL)
16586   {
16587     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16588
16589     node = node->next;
16590   }
16591
16592   /* restore special values from snapshot structure */
16593
16594   LoadEngineSnapshotValues_RND();
16595   LoadEngineSnapshotValues_EM();
16596 }
16597
16598 boolean CheckEngineSnapshot()
16599 {
16600   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16601           snapshot_level_nr == level_nr);
16602 }
16603
16604
16605 /* ---------- new game button stuff ---------------------------------------- */
16606
16607 /* graphic position values for game buttons */
16608 #define GAME_BUTTON_XSIZE       30
16609 #define GAME_BUTTON_YSIZE       30
16610 #define GAME_BUTTON_XPOS        5
16611 #define GAME_BUTTON_YPOS        215
16612 #define SOUND_BUTTON_XPOS       5
16613 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16614
16615 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16616 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16617 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16618 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16619 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16620 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16621
16622 static struct
16623 {
16624   int *x, *y;
16625   int gd_x, gd_y;
16626   int gadget_id;
16627   char *infotext;
16628 } gamebutton_info[NUM_GAME_BUTTONS] =
16629 {
16630 #if 1
16631   {
16632     &game.button.stop.x,        &game.button.stop.y,
16633     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16634     GAME_CTRL_ID_STOP,
16635     "stop game"
16636   },
16637   {
16638     &game.button.pause.x,       &game.button.pause.y,
16639     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16640     GAME_CTRL_ID_PAUSE,
16641     "pause game"
16642   },
16643   {
16644     &game.button.play.x,        &game.button.play.y,
16645     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16646     GAME_CTRL_ID_PLAY,
16647     "play game"
16648   },
16649   {
16650     &game.button.sound_music.x, &game.button.sound_music.y,
16651     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16652     SOUND_CTRL_ID_MUSIC,
16653     "background music on/off"
16654   },
16655   {
16656     &game.button.sound_loops.x, &game.button.sound_loops.y,
16657     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16658     SOUND_CTRL_ID_LOOPS,
16659     "sound loops on/off"
16660   },
16661   {
16662     &game.button.sound_simple.x,&game.button.sound_simple.y,
16663     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16664     SOUND_CTRL_ID_SIMPLE,
16665     "normal sounds on/off"
16666   }
16667 #else
16668   {
16669     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16670     GAME_CTRL_ID_STOP,
16671     "stop game"
16672   },
16673   {
16674     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16675     GAME_CTRL_ID_PAUSE,
16676     "pause game"
16677   },
16678   {
16679     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16680     GAME_CTRL_ID_PLAY,
16681     "play game"
16682   },
16683   {
16684     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16685     SOUND_CTRL_ID_MUSIC,
16686     "background music on/off"
16687   },
16688   {
16689     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16690     SOUND_CTRL_ID_LOOPS,
16691     "sound loops on/off"
16692   },
16693   {
16694     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16695     SOUND_CTRL_ID_SIMPLE,
16696     "normal sounds on/off"
16697   }
16698 #endif
16699 };
16700
16701 void CreateGameButtons()
16702 {
16703   int i;
16704
16705   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16706   {
16707     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16708     struct GadgetInfo *gi;
16709     int button_type;
16710     boolean checked;
16711     unsigned long event_mask;
16712     int x, y;
16713     int gd_xoffset, gd_yoffset;
16714     int gd_x1, gd_x2, gd_y1, gd_y2;
16715     int id = i;
16716
16717     x = DX + *gamebutton_info[i].x;
16718     y = DY + *gamebutton_info[i].y;
16719     gd_xoffset = gamebutton_info[i].gd_x;
16720     gd_yoffset = gamebutton_info[i].gd_y;
16721     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16722     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16723
16724     if (id == GAME_CTRL_ID_STOP ||
16725         id == GAME_CTRL_ID_PAUSE ||
16726         id == GAME_CTRL_ID_PLAY)
16727     {
16728       button_type = GD_TYPE_NORMAL_BUTTON;
16729       checked = FALSE;
16730       event_mask = GD_EVENT_RELEASED;
16731       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16732       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16733     }
16734     else
16735     {
16736       button_type = GD_TYPE_CHECK_BUTTON;
16737       checked =
16738         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16739          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16740          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16741       event_mask = GD_EVENT_PRESSED;
16742       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16743       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16744     }
16745
16746     gi = CreateGadget(GDI_CUSTOM_ID, id,
16747                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16748 #if 1
16749                       GDI_X, x,
16750                       GDI_Y, y,
16751 #else
16752                       GDI_X, DX + gd_xoffset,
16753                       GDI_Y, DY + gd_yoffset,
16754 #endif
16755                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16756                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16757                       GDI_TYPE, button_type,
16758                       GDI_STATE, GD_BUTTON_UNPRESSED,
16759                       GDI_CHECKED, checked,
16760                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16761                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16762                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16763                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16764                       GDI_DIRECT_DRAW, FALSE,
16765                       GDI_EVENT_MASK, event_mask,
16766                       GDI_CALLBACK_ACTION, HandleGameButtons,
16767                       GDI_END);
16768
16769     if (gi == NULL)
16770       Error(ERR_EXIT, "cannot create gadget");
16771
16772     game_gadget[id] = gi;
16773   }
16774 }
16775
16776 void FreeGameButtons()
16777 {
16778   int i;
16779
16780   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16781     FreeGadget(game_gadget[i]);
16782 }
16783
16784 static void MapGameButtons()
16785 {
16786   int i;
16787
16788   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16789     MapGadget(game_gadget[i]);
16790 }
16791
16792 void UnmapGameButtons()
16793 {
16794   int i;
16795
16796   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16797     UnmapGadget(game_gadget[i]);
16798 }
16799
16800 void RedrawGameButtons()
16801 {
16802   int i;
16803
16804   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16805     RedrawGadget(game_gadget[i]);
16806 }
16807
16808 static void HandleGameButtons(struct GadgetInfo *gi)
16809 {
16810   int id = gi->custom_id;
16811
16812   if (game_status != GAME_MODE_PLAYING)
16813     return;
16814
16815   switch (id)
16816   {
16817     case GAME_CTRL_ID_STOP:
16818       if (tape.playing)
16819         TapeStop();
16820       else
16821         RequestQuitGame(TRUE);
16822       break;
16823
16824     case GAME_CTRL_ID_PAUSE:
16825       if (options.network)
16826       {
16827 #if defined(NETWORK_AVALIABLE)
16828         if (tape.pausing)
16829           SendToServer_ContinuePlaying();
16830         else
16831           SendToServer_PausePlaying();
16832 #endif
16833       }
16834       else
16835         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16836       break;
16837
16838     case GAME_CTRL_ID_PLAY:
16839       if (tape.pausing)
16840       {
16841 #if defined(NETWORK_AVALIABLE)
16842         if (options.network)
16843           SendToServer_ContinuePlaying();
16844         else
16845 #endif
16846         {
16847           tape.pausing = FALSE;
16848           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16849         }
16850       }
16851       break;
16852
16853     case SOUND_CTRL_ID_MUSIC:
16854       if (setup.sound_music)
16855       { 
16856         setup.sound_music = FALSE;
16857         FadeMusic();
16858       }
16859       else if (audio.music_available)
16860       { 
16861         setup.sound = setup.sound_music = TRUE;
16862
16863         SetAudioMode(setup.sound);
16864
16865         PlayLevelMusic();
16866       }
16867       break;
16868
16869     case SOUND_CTRL_ID_LOOPS:
16870       if (setup.sound_loops)
16871         setup.sound_loops = FALSE;
16872       else if (audio.loops_available)
16873       {
16874         setup.sound = setup.sound_loops = TRUE;
16875         SetAudioMode(setup.sound);
16876       }
16877       break;
16878
16879     case SOUND_CTRL_ID_SIMPLE:
16880       if (setup.sound_simple)
16881         setup.sound_simple = FALSE;
16882       else if (audio.sound_available)
16883       {
16884         setup.sound = setup.sound_simple = TRUE;
16885         SetAudioMode(setup.sound);
16886       }
16887       break;
16888
16889     default:
16890       break;
16891   }
16892 }