rnd-20100630-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_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(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_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(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_FRAME                        35
198 #define GAME_PANEL_SHIELD_NORMAL                36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
200 #define GAME_PANEL_SHIELD_DEADLY                38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
202 #define GAME_PANEL_EXIT                         40
203 #define GAME_PANEL_EMC_MAGIC_BALL               41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
205 #define GAME_PANEL_LIGHT_SWITCH                 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
207 #define GAME_PANEL_TIMEGATE_SWITCH              45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
209 #define GAME_PANEL_SWITCHGATE_SWITCH            47
210 #define GAME_PANEL_EMC_LENSES                   48
211 #define GAME_PANEL_EMC_LENSES_TIME              49
212 #define GAME_PANEL_EMC_MAGNIFIER                50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
214 #define GAME_PANEL_BALLOON_SWITCH               52
215 #define GAME_PANEL_DYNABOMB_NUMBER              53
216 #define GAME_PANEL_DYNABOMB_SIZE                54
217 #define GAME_PANEL_DYNABOMB_POWER               55
218 #define GAME_PANEL_PENGUINS                     56
219 #define GAME_PANEL_SOKOBAN_OBJECTS              57
220 #define GAME_PANEL_SOKOBAN_FIELDS               58
221 #define GAME_PANEL_ROBOT_WHEEL                  59
222 #define GAME_PANEL_CONVEYOR_BELT_1              60
223 #define GAME_PANEL_CONVEYOR_BELT_2              61
224 #define GAME_PANEL_CONVEYOR_BELT_3              62
225 #define GAME_PANEL_CONVEYOR_BELT_4              63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
230 #define GAME_PANEL_MAGIC_WALL                   68
231 #define GAME_PANEL_MAGIC_WALL_TIME              69
232 #define GAME_PANEL_GRAVITY_STATE                70
233 #define GAME_PANEL_GRAPHIC_1                    71
234 #define GAME_PANEL_GRAPHIC_2                    72
235 #define GAME_PANEL_GRAPHIC_3                    73
236 #define GAME_PANEL_GRAPHIC_4                    74
237 #define GAME_PANEL_GRAPHIC_5                    75
238 #define GAME_PANEL_GRAPHIC_6                    76
239 #define GAME_PANEL_GRAPHIC_7                    77
240 #define GAME_PANEL_GRAPHIC_8                    78
241 #define GAME_PANEL_ELEMENT_1                    79
242 #define GAME_PANEL_ELEMENT_2                    80
243 #define GAME_PANEL_ELEMENT_3                    81
244 #define GAME_PANEL_ELEMENT_4                    82
245 #define GAME_PANEL_ELEMENT_5                    83
246 #define GAME_PANEL_ELEMENT_6                    84
247 #define GAME_PANEL_ELEMENT_7                    85
248 #define GAME_PANEL_ELEMENT_8                    86
249 #define GAME_PANEL_ELEMENT_COUNT_1              87
250 #define GAME_PANEL_ELEMENT_COUNT_2              88
251 #define GAME_PANEL_ELEMENT_COUNT_3              89
252 #define GAME_PANEL_ELEMENT_COUNT_4              90
253 #define GAME_PANEL_ELEMENT_COUNT_5              91
254 #define GAME_PANEL_ELEMENT_COUNT_6              92
255 #define GAME_PANEL_ELEMENT_COUNT_7              93
256 #define GAME_PANEL_ELEMENT_COUNT_8              94
257 #define GAME_PANEL_CE_SCORE_1                   95
258 #define GAME_PANEL_CE_SCORE_2                   96
259 #define GAME_PANEL_CE_SCORE_3                   97
260 #define GAME_PANEL_CE_SCORE_4                   98
261 #define GAME_PANEL_CE_SCORE_5                   99
262 #define GAME_PANEL_CE_SCORE_6                   100
263 #define GAME_PANEL_CE_SCORE_7                   101
264 #define GAME_PANEL_CE_SCORE_8                   102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
273 #define GAME_PANEL_PLAYER_NAME                  111
274 #define GAME_PANEL_LEVEL_NAME                   112
275 #define GAME_PANEL_LEVEL_AUTHOR                 113
276
277 #define NUM_GAME_PANEL_CONTROLS                 114
278
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289   int nr;
290
291   struct TextPosInfo *pos;
292   int type;
293
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING      3
884 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION   2
886 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF  -1
890 #define INITIAL_MOVE_DELAY_ON   0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED    32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED   4
896 #define MOVE_DELAY_MAX_SPEED    1
897
898 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN       (1)
907 #define MOVE_STEPSIZE_MAX       (TILEX)
908
909 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911
912 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
915                                  RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
917                                  RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
919                                  RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
921                                     (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
923                                  RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
926                                  RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
928                                  RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
932          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
935         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
936          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
937          (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p)                                         \
940         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
943         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
944          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
945          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
946          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
947          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
948          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
949          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
950          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
951          (e))
952
953 #define CAN_GROW_INTO(e)                                                \
954         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
957                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
968                                         (CAN_MOVE_INTO_ACID(e) &&       \
969                                          Feld[x][y] == EL_ACID) ||      \
970                                         (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
973                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
974                                         (condition) ||                  \
975                                         (CAN_MOVE_INTO_ACID(e) &&       \
976                                          Feld[x][y] == EL_ACID) ||      \
977                                         (DONT_COLLIDE_WITH(e) &&        \
978                                          IS_PLAYER(x, y) &&             \
979                                          !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
991         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1012                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1019         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1022         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1025         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1026                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1031                 (!IS_PLAYER(x, y) &&                                    \
1032                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1035         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP               0
1047 #define GAME_CTRL_ID_PAUSE              1
1048 #define GAME_CTRL_ID_PLAY               2
1049 #define SOUND_CTRL_ID_MUSIC             3
1050 #define SOUND_CTRL_ID_LOOPS             4
1051 #define SOUND_CTRL_ID_SIMPLE            5
1052
1053 #define NUM_GAME_BUTTONS                6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)                             \
1105         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1107         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1109         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1156 {                                                                       \
1157   if (recursion_loop_detected)                                          \
1158     return (rc);                                                        \
1159                                                                         \
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1161   {                                                                     \
1162     recursion_loop_detected = TRUE;                                     \
1163     recursion_loop_element = (e);                                       \
1164   }                                                                     \
1165                                                                         \
1166   recursion_loop_depth++;                                               \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END()                                  \
1170 {                                                                       \
1171   recursion_loop_depth--;                                               \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,                  0, 0 },
1484   { EL_BALLOON,                 0, 0 },
1485
1486   { EL_SOKOBAN_OBJECT,          2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1488   { EL_SATELLITE,               2, 0 },
1489   { EL_SP_DISK_YELLOW,          2, 0 },
1490
1491   { EL_UNDEFINED,               0, 0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,             2 },
1502   { EL_AMOEBA_DROPPING,         2 },
1503   { EL_QUICKSAND_FILLING,       1 },
1504   { EL_QUICKSAND_EMPTYING,      1 },
1505   { EL_QUICKSAND_FAST_FILLING,  2 },
1506   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507   { EL_MAGIC_WALL_FILLING,      2 },
1508   { EL_MAGIC_WALL_EMPTYING,     2 },
1509   { EL_BD_MAGIC_WALL_FILLING,   2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1511   { EL_DC_MAGIC_WALL_FILLING,   2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1513
1514   { EL_UNDEFINED,               0 },
1515 };
1516
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,                 1 },
1525   { EL_BD_DIAMOND,              1 },
1526   { EL_EMERALD_YELLOW,          1 },
1527   { EL_EMERALD_RED,             1 },
1528   { EL_EMERALD_PURPLE,          1 },
1529   { EL_DIAMOND,                 3 },
1530   { EL_SP_INFOTRON,             1 },
1531   { EL_PEARL,                   5 },
1532   { EL_CRYSTAL,                 8 },
1533
1534   { EL_UNDEFINED,               0 },
1535 };
1536
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1555
1556   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1558   { EL_SP_PORT_UP,                                                   MV_DOWN },
1559   { EL_SP_PORT_DOWN,                                         MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1575
1576   { EL_UNDEFINED,                       MV_NONE                              }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1584                                  IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1595                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1596                                      (y) += playfield_scan_delta_y)     \
1597                                 for ((x) = playfield_scan_start_x;      \
1598                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1599                                      (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609         EL_DYNAMITE;
1610 }
1611 #endif
1612
1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632
1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638   InitPlayfieldScanModeVars();
1639 }
1640
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648
1649   return TILEX / move_stepsize;
1650 }
1651
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653                                boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670     player->move_delay_value_next = -1;
1671
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675
1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697
1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710                                     group->choice_mode, 0,
1711                                     group->choice_pos);
1712
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715
1716     group->choice_pos++;
1717
1718     element = group->element_resolved[element_pos];
1719   }
1720
1721   return element;
1722 }
1723
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732         Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734         return;
1735       }
1736       else
1737       {
1738         stored_player[0].initial_element = element;
1739         stored_player[0].use_murphy = TRUE;
1740
1741         if (!level.use_artwork_element[0])
1742           stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753
1754     player->present = TRUE;
1755
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757                                 level.sp_block_last_field :
1758                                 level.block_last_field);
1759
1760     /* ---------- initialize player's last field block delay --------------- */
1761
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779         StorePlayer[jx][jy] = 0;
1780
1781       StorePlayer[x][y] = Feld[x][y];
1782
1783       if (options.debug)
1784       {
1785         printf("Player %d activated.\n", player->element_nr);
1786         printf("[Local player is %d and currently %s.]\n",
1787                local_player->element_nr,
1788                local_player->active ? "active" : "not active");
1789       }
1790     }
1791
1792     Feld[x][y] = EL_EMPTY;
1793
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809
1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899         Feld[x][y] = EL_AMOEBA_GROWING;
1900         Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1949         {
1950           game.belt_dir[belt_nr] = belt_dir;
1951           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952         }
1953         else    /* more than one switch -- set it like the first switch */
1954         {
1955           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956         }
1957       }
1958       break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1962       if (init_game)
1963         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1967       if (init_game)
1968         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981           game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026         if (CAN_MOVE(element))
2027           InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036         Feld[x][y] = GetElementFromGroupElement(element);
2037
2038         InitField(x, y, init_game);
2039       }
2040
2041       break;
2042   }
2043
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061
2062   InitField(x, y, init_game);
2063
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078
2079 #if 1
2080
2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085                           EL_EM_KEY_1 : EL_KEY_1);
2086
2087   return key_base_element + key_nr;
2088 }
2089
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093           player->inventory_element[player->inventory_size - 1] :
2094           player->inventory_infinite_element != EL_UNDEFINED ?
2095           player->inventory_infinite_element :
2096           player->dynabombs_left > 0 ?
2097           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098           EL_UNDEFINED);
2099 }
2100
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112     return (player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             player->inventory_infinite_element != EL_UNDEFINED ?
2115             player->inventory_infinite_element :
2116             player->dynabombs_left >= min_dynabombs_left ?
2117             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118             EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127             player->inventory_infinite_element :
2128             player->dynabombs_left >= min_dynabombs_left ?
2129             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130             player->inventory_size >= min_inventory_size ?
2131             player->inventory_element[inventory_pos] :
2132             EL_UNDEFINED);
2133   }
2134 }
2135
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146
2147   return compare_result;
2148 }
2149
2150 void InitGameControlValues()
2151 {
2152   int i;
2153
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226   int time = (local_player->LevelSolved ?
2227               local_player->LevelSolved_CountingTime :
2228               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->time :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->time_played :
2232               level.time == 0 ? TimePlayed : TimeLeft);
2233   int score = (local_player->LevelSolved ?
2234                local_player->LevelSolved_CountingScore :
2235                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                level.native_em_level->lev->score :
2237                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                level.native_sp_level->game_sp->score :
2239                local_player->score);
2240   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241               level.native_em_level->lev->required :
2242               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243               level.native_sp_level->game_sp->infotrons_still_needed :
2244               local_player->gems_still_needed);
2245   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                      level.native_em_level->lev->required > 0 :
2247                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249                      local_player->gems_still_needed > 0 ||
2250                      local_player->sokobanfields_still_needed > 0 ||
2251                      local_player->lights_still_needed > 0);
2252
2253   UpdatePlayfieldElementCount();
2254
2255   /* update game panel control values */
2256
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       /* only one player in Supaplex game engine */
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289           level.native_em_level->ply[i]->dynamite;
2290       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292           level.native_sp_level->game_sp->red_disk_count;
2293       else
2294         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295           stored_player[i].inventory_size;
2296
2297       if (stored_player[i].num_white_keys > 0)
2298         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299           EL_DC_KEY_WHITE;
2300
2301       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302         stored_player[i].num_white_keys;
2303     }
2304   }
2305   else
2306   {
2307     int player_nr = game.centered_player_nr;
2308
2309     for (k = 0; k < MAX_NUM_KEYS; k++)
2310     {
2311       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312       {
2313         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315             get_key_element_from_nr(k);
2316       }
2317       else if (stored_player[player_nr].key[k])
2318         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319           get_key_element_from_nr(k);
2320     }
2321
2322     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         level.native_em_level->ply[player_nr]->dynamite;
2325     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327         level.native_sp_level->game_sp->red_disk_count;
2328     else
2329       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330         stored_player[player_nr].inventory_size;
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340   {
2341     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342       get_inventory_element_from_pos(local_player, i);
2343     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344       get_inventory_element_from_pos(local_player, -i - 1);
2345   }
2346
2347   game_panel_controls[GAME_PANEL_SCORE].value = score;
2348   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349
2350   game_panel_controls[GAME_PANEL_TIME].value = time;
2351
2352   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355
2356   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360      EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362     local_player->shield_normal_time_left;
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365      EL_EMPTY);
2366   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367     local_player->shield_deadly_time_left;
2368
2369   game_panel_controls[GAME_PANEL_EXIT].value =
2370     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376      EL_EMC_MAGIC_BALL_SWITCH);
2377
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381     game.light_time_left;
2382
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386     game.timegate_time_left;
2387
2388   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390
2391   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394     game.lenses_time_left;
2395
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399     game.magnify_time_left;
2400
2401   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2403      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2405      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2406      EL_BALLOON_SWITCH_NONE);
2407
2408   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409     local_player->dynabomb_count;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411     local_player->dynabomb_size;
2412   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414
2415   game_panel_controls[GAME_PANEL_PENGUINS].value =
2416     local_player->friends_still_needed;
2417
2418   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419     local_player->sokobanfields_still_needed;
2420   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421     local_player->sokobanfields_still_needed;
2422
2423   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425
2426   for (i = 0; i < NUM_BELTS; i++)
2427   {
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433   }
2434
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438     game.magic_wall_time_left;
2439
2440 #if USE_PLAYER_GRAVITY
2441   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442     local_player->gravity;
2443 #else
2444   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446
2447   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449
2450   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453        game.panel.element[i].id : EL_UNDEFINED);
2454
2455   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458        element_info[game.panel.element_count[i].id].element_count : 0);
2459
2460   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463        element_info[game.panel.ce_score[i].id].collect_score : 0);
2464
2465   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468        element_info[game.panel.ce_score_element[i].id].collect_score :
2469        EL_UNDEFINED);
2470
2471   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474
2475   /* update game panel control frames */
2476
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480
2481     if (gpc->type == TYPE_ELEMENT)
2482     {
2483       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484       {
2485         int last_anim_random_frame = gfx.anim_random_frame;
2486         int element = gpc->value;
2487         int graphic = el2panelimg(element);
2488
2489         if (gpc->value != gpc->last_value)
2490         {
2491           gpc->gfx_frame = 0;
2492           gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494         else
2495         {
2496           gpc->gfx_frame++;
2497
2498           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500             gpc->gfx_random = INIT_GFX_RANDOM();
2501         }
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = gpc->gfx_random;
2505
2506         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507           gpc->gfx_frame = element_info[element].collect_score;
2508
2509         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510                                               gpc->gfx_frame);
2511
2512         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513           gfx.anim_random_frame = last_anim_random_frame;
2514       }
2515     }
2516   }
2517 }
2518
2519 void DisplayGameControlValues()
2520 {
2521   boolean redraw_panel = FALSE;
2522   int i;
2523
2524   for (i = 0; game_panel_controls[i].nr != -1; i++)
2525   {
2526     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527
2528     if (PANEL_DEACTIVATED(gpc->pos))
2529       continue;
2530
2531     if (gpc->value == gpc->last_value &&
2532         gpc->frame == gpc->last_frame)
2533       continue;
2534
2535     redraw_panel = TRUE;
2536   }
2537
2538   if (!redraw_panel)
2539     return;
2540
2541   /* copy default game door content to main double buffer */
2542 #if 1
2543   /* !!! CHECK AGAIN !!! */
2544   SetPanelBackground();
2545   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2547 #else
2548   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2550 #endif
2551
2552   /* redraw game control buttons */
2553 #if 1
2554   RedrawGameButtons();
2555 #else
2556   UnmapGameButtons();
2557   MapGameButtons();
2558 #endif
2559
2560   game_status = GAME_MODE_PSEUDO_PANEL;
2561
2562 #if 1
2563   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2564 #else
2565   for (i = 0; game_panel_controls[i].nr != -1; i++)
2566 #endif
2567   {
2568 #if 1
2569     int nr = game_panel_order[i].nr;
2570     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2571 #else
2572     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2573     int nr = gpc->nr;
2574 #endif
2575     struct TextPosInfo *pos = gpc->pos;
2576     int type = gpc->type;
2577     int value = gpc->value;
2578     int frame = gpc->frame;
2579 #if 0
2580     int last_value = gpc->last_value;
2581     int last_frame = gpc->last_frame;
2582 #endif
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591 #if 0
2592     if (value == last_value && frame == last_frame)
2593       continue;
2594 #endif
2595
2596     gpc->last_value = value;
2597     gpc->last_frame = frame;
2598
2599 #if 0
2600     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2601 #endif
2602
2603     if (type == TYPE_INTEGER)
2604     {
2605       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606           nr == GAME_PANEL_TIME)
2607       {
2608         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2609
2610         if (use_dynamic_size)           /* use dynamic number of digits */
2611         {
2612           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614           int size2 = size1 + 1;
2615           int font1 = pos->font;
2616           int font2 = pos->font_alt;
2617
2618           size = (value < value_change ? size1 : size2);
2619           font = (value < value_change ? font1 : font2);
2620
2621 #if 0
2622           /* clear background if value just changed its size (dynamic digits) */
2623           if ((last_value < value_change) != (value < value_change))
2624           {
2625             int width1 = size1 * getFontWidth(font1);
2626             int width2 = size2 * getFontWidth(font2);
2627             int max_width = MAX(width1, width2);
2628             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2629
2630             pos->width = max_width;
2631
2632             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633                                        max_width, max_height);
2634           }
2635 #endif
2636         }
2637       }
2638
2639 #if 1
2640       /* correct text size if "digits" is zero or less */
2641       if (size <= 0)
2642         size = strlen(int2str(value, size));
2643
2644       /* dynamically correct text alignment */
2645       pos->width = size * getFontWidth(font);
2646 #endif
2647
2648       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649                   int2str(value, size), font, mask_mode);
2650     }
2651     else if (type == TYPE_ELEMENT)
2652     {
2653       int element, graphic;
2654       Bitmap *src_bitmap;
2655       int src_x, src_y;
2656       int width, height;
2657       int dst_x = PANEL_XPOS(pos);
2658       int dst_y = PANEL_YPOS(pos);
2659
2660 #if 1
2661       if (value != EL_UNDEFINED && value != EL_EMPTY)
2662       {
2663         element = value;
2664         graphic = el2panelimg(value);
2665
2666         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2667
2668 #if 1
2669         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2670           size = TILESIZE;
2671 #endif
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2674                               &src_x, &src_y);
2675
2676         width  = graphic_info[graphic].width  * size / TILESIZE;
2677         height = graphic_info[graphic].height * size / TILESIZE;
2678
2679         if (draw_masked)
2680         {
2681           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682                         dst_x - src_x, dst_y - src_y);
2683           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2684                            dst_x, dst_y);
2685         }
2686         else
2687         {
2688           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2689                      dst_x, dst_y);
2690         }
2691       }
2692 #else
2693       if (value == EL_UNDEFINED || value == EL_EMPTY)
2694       {
2695         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696         graphic = el2panelimg(element);
2697
2698         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2701       }
2702       else
2703       {
2704         element = value;
2705         graphic = el2panelimg(value);
2706
2707         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2708       }
2709
2710       width  = graphic_info[graphic].width  * size / TILESIZE;
2711       height = graphic_info[graphic].height * size / TILESIZE;
2712
2713       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2714 #endif
2715     }
2716     else if (type == TYPE_STRING)
2717     {
2718       boolean active = (value != 0);
2719       char *state_normal = "off";
2720       char *state_active = "on";
2721       char *state = (active ? state_active : state_normal);
2722       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2724                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2725                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2726
2727       if (nr == GAME_PANEL_GRAVITY_STATE)
2728       {
2729         int font1 = pos->font;          /* (used for normal state) */
2730         int font2 = pos->font_alt;      /* (used for active state) */
2731 #if 0
2732         int size1 = strlen(state_normal);
2733         int size2 = strlen(state_active);
2734         int width1 = size1 * getFontWidth(font1);
2735         int width2 = size2 * getFontWidth(font2);
2736         int max_width = MAX(width1, width2);
2737         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2738
2739         pos->width = max_width;
2740
2741         /* clear background for values that may have changed its size */
2742         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743                                    max_width, max_height);
2744 #endif
2745
2746         font = (active ? font2 : font1);
2747       }
2748
2749       if (s != NULL)
2750       {
2751         char *s_cut;
2752
2753 #if 1
2754         if (size <= 0)
2755         {
2756           /* don't truncate output if "chars" is zero or less */
2757           size = strlen(s);
2758
2759           /* dynamically correct text alignment */
2760           pos->width = size * getFontWidth(font);
2761         }
2762 #endif
2763
2764         s_cut = getStringCopyN(s, size);
2765
2766         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                     s_cut, font, mask_mode);
2768
2769         free(s_cut);
2770       }
2771     }
2772
2773     redraw_mask |= REDRAW_DOOR_1;
2774   }
2775
2776   game_status = GAME_MODE_PLAYING;
2777 }
2778
2779 void UpdateAndDisplayGameControlValues()
2780 {
2781   if (tape.warp_forward)
2782     return;
2783
2784   UpdateGameControlValues();
2785   DisplayGameControlValues();
2786 }
2787
2788 void DrawGameValue_Emeralds(int value)
2789 {
2790   struct TextPosInfo *pos = &game.panel.gems;
2791 #if 1
2792   int font_nr = pos->font;
2793 #else
2794   int font_nr = FONT_TEXT_2;
2795 #endif
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Dynamite(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.inventory_count;
2814 #if 1
2815   int font_nr = pos->font;
2816 #else
2817   int font_nr = FONT_TEXT_2;
2818 #endif
2819   int font_width = getFontWidth(font_nr);
2820   int chars = pos->size;
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   pos->width = chars * font_width;
2830
2831   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2832 }
2833
2834 void DrawGameValue_Score(int value)
2835 {
2836   struct TextPosInfo *pos = &game.panel.score;
2837 #if 1
2838   int font_nr = pos->font;
2839 #else
2840   int font_nr = FONT_TEXT_2;
2841 #endif
2842   int font_width = getFontWidth(font_nr);
2843   int chars = pos->size;
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849   if (PANEL_DEACTIVATED(pos))
2850     return;
2851
2852   pos->width = chars * font_width;
2853
2854   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2855 }
2856
2857 void DrawGameValue_Time(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.time;
2860   static int last_value = -1;
2861   int chars1 = 3;
2862   int chars2 = 4;
2863   int chars = pos->size;
2864 #if 1
2865   int font1_nr = pos->font;
2866   int font2_nr = pos->font_alt;
2867 #else
2868   int font1_nr = FONT_TEXT_2;
2869   int font2_nr = FONT_TEXT_1;
2870 #endif
2871   int font_nr = font1_nr;
2872   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2873
2874 #if 1
2875   return;       /* !!! USE NEW STUFF !!! */
2876 #endif
2877
2878   if (PANEL_DEACTIVATED(pos))
2879     return;
2880
2881   if (use_dynamic_chars)                /* use dynamic number of chars */
2882   {
2883     chars   = (value < 1000 ? chars1   : chars2);
2884     font_nr = (value < 1000 ? font1_nr : font2_nr);
2885   }
2886
2887   /* clear background if value just changed its size (dynamic chars only) */
2888   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2889   {
2890     int width1 = chars1 * getFontWidth(font1_nr);
2891     int width2 = chars2 * getFontWidth(font2_nr);
2892     int max_width = MAX(width1, width2);
2893     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2894
2895     pos->width = max_width;
2896
2897     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898                                max_width, max_height);
2899   }
2900
2901   pos->width = chars * getFontWidth(font_nr);
2902
2903   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2904
2905   last_value = value;
2906 }
2907
2908 void DrawGameValue_Level(int value)
2909 {
2910   struct TextPosInfo *pos = &game.panel.level_number;
2911   int chars1 = 2;
2912   int chars2 = 3;
2913   int chars = pos->size;
2914 #if 1
2915   int font1_nr = pos->font;
2916   int font2_nr = pos->font_alt;
2917 #else
2918   int font1_nr = FONT_TEXT_2;
2919   int font2_nr = FONT_TEXT_1;
2920 #endif
2921   int font_nr = font1_nr;
2922   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2923
2924 #if 1
2925   return;       /* !!! USE NEW STUFF !!! */
2926 #endif
2927
2928   if (PANEL_DEACTIVATED(pos))
2929     return;
2930
2931   if (use_dynamic_chars)                /* use dynamic number of chars */
2932   {
2933     chars   = (level_nr < 100 ? chars1   : chars2);
2934     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2935   }
2936
2937   pos->width = chars * getFontWidth(font_nr);
2938
2939   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2940 }
2941
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2943 {
2944 #if 0
2945   struct TextPosInfo *pos = &game.panel.keys;
2946 #endif
2947 #if 0
2948   int base_key_graphic = EL_KEY_1;
2949 #endif
2950   int i;
2951
2952 #if 1
2953   return;       /* !!! USE NEW STUFF !!! */
2954 #endif
2955
2956 #if 0
2957   if (PANEL_DEACTIVATED(pos))
2958     return;
2959 #endif
2960
2961 #if 0
2962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963     base_key_graphic = EL_EM_KEY_1;
2964 #endif
2965
2966 #if 0
2967   pos->width = 4 * MINI_TILEX;
2968 #endif
2969
2970 #if 1
2971   for (i = 0; i < MAX_NUM_KEYS; i++)
2972 #else
2973   /* currently only 4 of 8 possible keys are displayed */
2974   for (i = 0; i < STD_NUM_KEYS; i++)
2975 #endif
2976   {
2977 #if 1
2978     struct TextPosInfo *pos = &game.panel.key[i];
2979 #endif
2980     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981     int src_y = DOOR_GFX_PAGEY1 + 123;
2982 #if 1
2983     int dst_x = PANEL_XPOS(pos);
2984     int dst_y = PANEL_YPOS(pos);
2985 #else
2986     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987     int dst_y = PANEL_YPOS(pos);
2988 #endif
2989
2990 #if 1
2991     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2993                    EL_KEY_1) + i;
2994     int graphic = el2edimg(element);
2995 #endif
2996
2997 #if 1
2998     if (PANEL_DEACTIVATED(pos))
2999       continue;
3000 #endif
3001
3002 #if 0
3003     /* masked blit with tiles from half-size scaled bitmap does not work yet
3004        (no mask bitmap created for these sizes after loading and scaling) --
3005        solution: load without creating mask, scale, then create final mask */
3006
3007     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3009
3010     if (key[i])
3011     {
3012 #if 0
3013       int graphic = el2edimg(base_key_graphic + i);
3014 #endif
3015       Bitmap *src_bitmap;
3016       int src_x, src_y;
3017
3018       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3019
3020       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021                     dst_x - src_x, dst_y - src_y);
3022       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3023                        dst_x, dst_y);
3024     }
3025 #else
3026 #if 1
3027     if (key[i])
3028       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3029     else
3030       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3032 #else
3033     if (key[i])
3034       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3035     else
3036       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3038 #endif
3039 #endif
3040   }
3041 }
3042
3043 #else
3044
3045 void DrawGameValue_Emeralds(int value)
3046 {
3047   int font_nr = FONT_TEXT_2;
3048   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3049
3050   if (PANEL_DEACTIVATED(game.panel.gems))
3051     return;
3052
3053   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3054 }
3055
3056 void DrawGameValue_Dynamite(int value)
3057 {
3058   int font_nr = FONT_TEXT_2;
3059   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060
3061   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3062     return;
3063
3064   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3065 }
3066
3067 void DrawGameValue_Score(int value)
3068 {
3069   int font_nr = FONT_TEXT_2;
3070   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3071
3072   if (PANEL_DEACTIVATED(game.panel.score))
3073     return;
3074
3075   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3076 }
3077
3078 void DrawGameValue_Time(int value)
3079 {
3080   int font1_nr = FONT_TEXT_2;
3081 #if 1
3082   int font2_nr = FONT_TEXT_1;
3083 #else
3084   int font2_nr = FONT_LEVEL_NUMBER;
3085 #endif
3086   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3088
3089   if (PANEL_DEACTIVATED(game.panel.time))
3090     return;
3091
3092   /* clear background if value just changed its size */
3093   if (value == 999 || value == 1000)
3094     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3095
3096   if (value < 1000)
3097     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3098   else
3099     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3100 }
3101
3102 void DrawGameValue_Level(int value)
3103 {
3104   int font1_nr = FONT_TEXT_2;
3105 #if 1
3106   int font2_nr = FONT_TEXT_1;
3107 #else
3108   int font2_nr = FONT_LEVEL_NUMBER;
3109 #endif
3110
3111   if (PANEL_DEACTIVATED(game.panel.level))
3112     return;
3113
3114   if (level_nr < 100)
3115     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3116   else
3117     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3118 }
3119
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3121 {
3122   int base_key_graphic = EL_KEY_1;
3123   int i;
3124
3125   if (PANEL_DEACTIVATED(game.panel.keys))
3126     return;
3127
3128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129     base_key_graphic = EL_EM_KEY_1;
3130
3131   /* currently only 4 of 8 possible keys are displayed */
3132   for (i = 0; i < STD_NUM_KEYS; i++)
3133   {
3134     int x = XX_KEYS + i * MINI_TILEX;
3135     int y = YY_KEYS;
3136
3137     if (key[i])
3138       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3139     else
3140       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3142   }
3143 }
3144
3145 #endif
3146
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3148                        int key_bits)
3149 {
3150   int key[MAX_NUM_KEYS];
3151   int i;
3152
3153   /* prevent EM engine from updating time/score values parallel to GameWon() */
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155       local_player->LevelSolved)
3156     return;
3157
3158   for (i = 0; i < MAX_NUM_KEYS; i++)
3159     key[i] = key_bits & (1 << i);
3160
3161   DrawGameValue_Level(level_nr);
3162
3163   DrawGameValue_Emeralds(emeralds);
3164   DrawGameValue_Dynamite(dynamite);
3165   DrawGameValue_Score(score);
3166   DrawGameValue_Time(time);
3167
3168   DrawGameValue_Keys(key);
3169 }
3170
3171 void UpdateGameDoorValues()
3172 {
3173   UpdateGameControlValues();
3174 }
3175
3176 void DrawGameDoorValues()
3177 {
3178   DisplayGameControlValues();
3179 }
3180
3181 void DrawGameDoorValues_OLD()
3182 {
3183   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184   int dynamite_value = 0;
3185   int score_value = (local_player->LevelSolved ? local_player->score_final :
3186                      local_player->score);
3187   int gems_value = local_player->gems_still_needed;
3188   int key_bits = 0;
3189   int i, j;
3190
3191   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3192   {
3193     DrawGameDoorValues_EM();
3194
3195     return;
3196   }
3197
3198   if (game.centered_player_nr == -1)
3199   {
3200     for (i = 0; i < MAX_PLAYERS; i++)
3201     {
3202       for (j = 0; j < MAX_NUM_KEYS; j++)
3203         if (stored_player[i].key[j])
3204           key_bits |= (1 << j);
3205
3206       dynamite_value += stored_player[i].inventory_size;
3207     }
3208   }
3209   else
3210   {
3211     int player_nr = game.centered_player_nr;
3212
3213     for (i = 0; i < MAX_NUM_KEYS; i++)
3214       if (stored_player[player_nr].key[i])
3215         key_bits |= (1 << i);
3216
3217     dynamite_value = stored_player[player_nr].inventory_size;
3218   }
3219
3220   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3221                     key_bits);
3222 }
3223
3224
3225 /*
3226   =============================================================================
3227   InitGameEngine()
3228   -----------------------------------------------------------------------------
3229   initialize game engine due to level / tape version number
3230   =============================================================================
3231 */
3232
3233 static void InitGameEngine()
3234 {
3235   int i, j, k, l, x, y;
3236
3237   /* set game engine from tape file when re-playing, else from level file */
3238   game.engine_version = (tape.playing ? tape.engine_version :
3239                          level.game_version);
3240
3241   /* ---------------------------------------------------------------------- */
3242   /* set flags for bugs and changes according to active game engine version */
3243   /* ---------------------------------------------------------------------- */
3244
3245   /*
3246     Summary of bugfix/change:
3247     Fixed handling for custom elements that change when pushed by the player.
3248
3249     Fixed/changed in version:
3250     3.1.0
3251
3252     Description:
3253     Before 3.1.0, custom elements that "change when pushing" changed directly
3254     after the player started pushing them (until then handled in "DigField()").
3255     Since 3.1.0, these custom elements are not changed until the "pushing"
3256     move of the element is finished (now handled in "ContinueMoving()").
3257
3258     Affected levels/tapes:
3259     The first condition is generally needed for all levels/tapes before version
3260     3.1.0, which might use the old behaviour before it was changed; known tapes
3261     that are affected are some tapes from the level set "Walpurgis Gardens" by
3262     Jamie Cullen.
3263     The second condition is an exception from the above case and is needed for
3264     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265     above (including some development versions of 3.1.0), but before it was
3266     known that this change would break tapes like the above and was fixed in
3267     3.1.1, so that the changed behaviour was active although the engine version
3268     while recording maybe was before 3.1.0. There is at least one tape that is
3269     affected by this exception, which is the tape for the one-level set "Bug
3270     Machine" by Juergen Bonhagen.
3271   */
3272
3273   game.use_change_when_pushing_bug =
3274     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3275      !(tape.playing &&
3276        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3278
3279   /*
3280     Summary of bugfix/change:
3281     Fixed handling for blocking the field the player leaves when moving.
3282
3283     Fixed/changed in version:
3284     3.1.1
3285
3286     Description:
3287     Before 3.1.1, when "block last field when moving" was enabled, the field
3288     the player is leaving when moving was blocked for the time of the move,
3289     and was directly unblocked afterwards. This resulted in the last field
3290     being blocked for exactly one less than the number of frames of one player
3291     move. Additionally, even when blocking was disabled, the last field was
3292     blocked for exactly one frame.
3293     Since 3.1.1, due to changes in player movement handling, the last field
3294     is not blocked at all when blocking is disabled. When blocking is enabled,
3295     the last field is blocked for exactly the number of frames of one player
3296     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297     last field is blocked for exactly one more than the number of frames of
3298     one player move.
3299
3300     Affected levels/tapes:
3301     (!!! yet to be determined -- probably many !!!)
3302   */
3303
3304   game.use_block_last_field_bug =
3305     (game.engine_version < VERSION_IDENT(3,1,1,0));
3306
3307   /*
3308     Summary of bugfix/change:
3309     Changed behaviour of CE changes with multiple changes per single frame.
3310
3311     Fixed/changed in version:
3312     3.2.0-6
3313
3314     Description:
3315     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316     This resulted in race conditions where CEs seem to behave strange in some
3317     situations (where triggered CE changes were just skipped because there was
3318     already a CE change on that tile in the playfield in that engine frame).
3319     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320     (The number of changes per frame must be limited in any case, because else
3321     it is easily possible to define CE changes that would result in an infinite
3322     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323     should be set large enough so that it would only be reached in cases where
3324     the corresponding CE change conditions run into a loop. Therefore, it seems
3325     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326     maximal number of change pages for custom elements.)
3327
3328     Affected levels/tapes:
3329     Probably many.
3330   */
3331
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333   game.max_num_changes_per_frame = 1;
3334 #else
3335   game.max_num_changes_per_frame =
3336     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3337 #endif
3338
3339   /* ---------------------------------------------------------------------- */
3340
3341   /* default scan direction: scan playfield from top/left to bottom/right */
3342   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3343
3344   /* dynamically adjust element properties according to game engine version */
3345   InitElementPropertiesEngine(game.engine_version);
3346
3347 #if 0
3348   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349   printf("          tape version == %06d [%s] [file: %06d]\n",
3350          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3351          tape.file_version);
3352   printf("       => game.engine_version == %06d\n", game.engine_version);
3353 #endif
3354
3355   /* ---------- initialize player's initial move delay --------------------- */
3356
3357   /* dynamically adjust player properties according to level information */
3358   for (i = 0; i < MAX_PLAYERS; i++)
3359     game.initial_move_delay_value[i] =
3360       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3361
3362   /* dynamically adjust player properties according to game engine version */
3363   for (i = 0; i < MAX_PLAYERS; i++)
3364     game.initial_move_delay[i] =
3365       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366        game.initial_move_delay_value[i] : 0);
3367
3368   /* ---------- initialize player's initial push delay --------------------- */
3369
3370   /* dynamically adjust player properties according to game engine version */
3371   game.initial_push_delay_value =
3372     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3373
3374   /* ---------- initialize changing elements ------------------------------- */
3375
3376   /* initialize changing elements information */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378   {
3379     struct ElementInfo *ei = &element_info[i];
3380
3381     /* this pointer might have been changed in the level editor */
3382     ei->change = &ei->change_page[0];
3383
3384     if (!IS_CUSTOM_ELEMENT(i))
3385     {
3386       ei->change->target_element = EL_EMPTY_SPACE;
3387       ei->change->delay_fixed = 0;
3388       ei->change->delay_random = 0;
3389       ei->change->delay_frames = 1;
3390     }
3391
3392     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3393     {
3394       ei->has_change_event[j] = FALSE;
3395
3396       ei->event_page_nr[j] = 0;
3397       ei->event_page[j] = &ei->change_page[0];
3398     }
3399   }
3400
3401   /* add changing elements from pre-defined list */
3402   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3403   {
3404     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405     struct ElementInfo *ei = &element_info[ch_delay->element];
3406
3407     ei->change->target_element       = ch_delay->target_element;
3408     ei->change->delay_fixed          = ch_delay->change_delay;
3409
3410     ei->change->pre_change_function  = ch_delay->pre_change_function;
3411     ei->change->change_function      = ch_delay->change_function;
3412     ei->change->post_change_function = ch_delay->post_change_function;
3413
3414     ei->change->can_change = TRUE;
3415     ei->change->can_change_or_has_action = TRUE;
3416
3417     ei->has_change_event[CE_DELAY] = TRUE;
3418
3419     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3421   }
3422
3423   /* ---------- initialize internal run-time variables --------------------- */
3424
3425   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3426   {
3427     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3428
3429     for (j = 0; j < ei->num_change_pages; j++)
3430     {
3431       ei->change_page[j].can_change_or_has_action =
3432         (ei->change_page[j].can_change |
3433          ei->change_page[j].has_action);
3434     }
3435   }
3436
3437   /* add change events from custom element configuration */
3438   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3439   {
3440     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3441
3442     for (j = 0; j < ei->num_change_pages; j++)
3443     {
3444       if (!ei->change_page[j].can_change_or_has_action)
3445         continue;
3446
3447       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3448       {
3449         /* only add event page for the first page found with this event */
3450         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3451         {
3452           ei->has_change_event[k] = TRUE;
3453
3454           ei->event_page_nr[k] = j;
3455           ei->event_page[k] = &ei->change_page[j];
3456         }
3457       }
3458     }
3459   }
3460
3461 #if 1
3462   /* ---------- initialize reference elements in change conditions --------- */
3463
3464   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3465   {
3466     int element = EL_CUSTOM_START + i;
3467     struct ElementInfo *ei = &element_info[element];
3468
3469     for (j = 0; j < ei->num_change_pages; j++)
3470     {
3471       int trigger_element = ei->change_page[j].initial_trigger_element;
3472
3473       if (trigger_element >= EL_PREV_CE_8 &&
3474           trigger_element <= EL_NEXT_CE_8)
3475         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3476
3477       ei->change_page[j].trigger_element = trigger_element;
3478     }
3479   }
3480 #endif
3481
3482   /* ---------- initialize run-time trigger player and element ------------- */
3483
3484   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3485   {
3486     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3487
3488     for (j = 0; j < ei->num_change_pages; j++)
3489     {
3490       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494       ei->change_page[j].actual_trigger_ce_value = 0;
3495       ei->change_page[j].actual_trigger_ce_score = 0;
3496     }
3497   }
3498
3499   /* ---------- initialize trigger events ---------------------------------- */
3500
3501   /* initialize trigger events information */
3502   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504       trigger_events[i][j] = FALSE;
3505
3506   /* add trigger events from element change event properties */
3507   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3508   {
3509     struct ElementInfo *ei = &element_info[i];
3510
3511     for (j = 0; j < ei->num_change_pages; j++)
3512     {
3513       if (!ei->change_page[j].can_change_or_has_action)
3514         continue;
3515
3516       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3517       {
3518         int trigger_element = ei->change_page[j].trigger_element;
3519
3520         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3521         {
3522           if (ei->change_page[j].has_event[k])
3523           {
3524             if (IS_GROUP_ELEMENT(trigger_element))
3525             {
3526               struct ElementGroupInfo *group =
3527                 element_info[trigger_element].group;
3528
3529               for (l = 0; l < group->num_elements_resolved; l++)
3530                 trigger_events[group->element_resolved[l]][k] = TRUE;
3531             }
3532             else if (trigger_element == EL_ANY_ELEMENT)
3533               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534                 trigger_events[l][k] = TRUE;
3535             else
3536               trigger_events[trigger_element][k] = TRUE;
3537           }
3538         }
3539       }
3540     }
3541   }
3542
3543   /* ---------- initialize push delay -------------------------------------- */
3544
3545   /* initialize push delay values to default */
3546   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3547   {
3548     if (!IS_CUSTOM_ELEMENT(i))
3549     {
3550       /* set default push delay values (corrected since version 3.0.7-1) */
3551       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3552       {
3553         element_info[i].push_delay_fixed = 2;
3554         element_info[i].push_delay_random = 8;
3555       }
3556       else
3557       {
3558         element_info[i].push_delay_fixed = 8;
3559         element_info[i].push_delay_random = 8;
3560       }
3561     }
3562   }
3563
3564   /* set push delay value for certain elements from pre-defined list */
3565   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3566   {
3567     int e = push_delay_list[i].element;
3568
3569     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3570     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3571   }
3572
3573   /* set push delay value for Supaplex elements for newer engine versions */
3574   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3575   {
3576     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577     {
3578       if (IS_SP_ELEMENT(i))
3579       {
3580         /* set SP push delay to just enough to push under a falling zonk */
3581         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3582
3583         element_info[i].push_delay_fixed  = delay;
3584         element_info[i].push_delay_random = 0;
3585       }
3586     }
3587   }
3588
3589   /* ---------- initialize move stepsize ----------------------------------- */
3590
3591   /* initialize move stepsize values to default */
3592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593     if (!IS_CUSTOM_ELEMENT(i))
3594       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3595
3596   /* set move stepsize value for certain elements from pre-defined list */
3597   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3598   {
3599     int e = move_stepsize_list[i].element;
3600
3601     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3602   }
3603
3604   /* ---------- initialize collect score ----------------------------------- */
3605
3606   /* initialize collect score values for custom elements from initial value */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_score = element_info[i].collect_score_initial;
3610
3611   /* ---------- initialize collect count ----------------------------------- */
3612
3613   /* initialize collect count values for non-custom elements */
3614   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615     if (!IS_CUSTOM_ELEMENT(i))
3616       element_info[i].collect_count_initial = 0;
3617
3618   /* add collect count values for all elements from pre-defined list */
3619   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620     element_info[collect_count_list[i].element].collect_count_initial =
3621       collect_count_list[i].count;
3622
3623   /* ---------- initialize access direction -------------------------------- */
3624
3625   /* initialize access direction values to default (access from every side) */
3626   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627     if (!IS_CUSTOM_ELEMENT(i))
3628       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3629
3630   /* set access direction value for certain elements from pre-defined list */
3631   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632     element_info[access_direction_list[i].element].access_direction =
3633       access_direction_list[i].direction;
3634
3635   /* ---------- initialize explosion content ------------------------------- */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637   {
3638     if (IS_CUSTOM_ELEMENT(i))
3639       continue;
3640
3641     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3642     {
3643       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3644
3645       element_info[i].content.e[x][y] =
3646         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648          i == EL_PLAYER_3 ? EL_EMERALD :
3649          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650          i == EL_MOLE ? EL_EMERALD_RED :
3651          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656          i == EL_WALL_EMERALD ? EL_EMERALD :
3657          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662          i == EL_WALL_PEARL ? EL_PEARL :
3663          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3664          EL_EMPTY);
3665     }
3666   }
3667
3668   /* ---------- initialize recursion detection ------------------------------ */
3669   recursion_loop_depth = 0;
3670   recursion_loop_detected = FALSE;
3671   recursion_loop_element = EL_UNDEFINED;
3672
3673   /* ---------- initialize graphics engine ---------------------------------- */
3674   game.scroll_delay_value =
3675     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3677   game.scroll_delay_value =
3678     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3679 }
3680
3681 int get_num_special_action(int element, int action_first, int action_last)
3682 {
3683   int num_special_action = 0;
3684   int i, j;
3685
3686   for (i = action_first; i <= action_last; i++)
3687   {
3688     boolean found = FALSE;
3689
3690     for (j = 0; j < NUM_DIRECTIONS; j++)
3691       if (el_act_dir2img(element, i, j) !=
3692           el_act_dir2img(element, ACTION_DEFAULT, j))
3693         found = TRUE;
3694
3695     if (found)
3696       num_special_action++;
3697     else
3698       break;
3699   }
3700
3701   return num_special_action;
3702 }
3703
3704
3705 /*
3706   =============================================================================
3707   InitGame()
3708   -----------------------------------------------------------------------------
3709   initialize and start new game
3710   =============================================================================
3711 */
3712
3713 void InitGame()
3714 {
3715   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3716   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3717   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3718 #if 0
3719   boolean do_fading = (game_status == GAME_MODE_MAIN);
3720 #endif
3721 #if 1
3722   int initial_move_dir = MV_DOWN;
3723 #else
3724   int initial_move_dir = MV_NONE;
3725 #endif
3726   int i, j, x, y;
3727
3728   game_status = GAME_MODE_PLAYING;
3729
3730 #if 1
3731   /* needed if different viewport properties defined for playing */
3732   ChangeViewportPropertiesIfNeeded();
3733 #endif
3734
3735 #if 1
3736   DrawCompleteVideoDisplay();
3737 #endif
3738
3739   InitGameEngine();
3740   InitGameControlValues();
3741
3742   /* don't play tapes over network */
3743   network_playing = (options.network && !tape.playing);
3744
3745   for (i = 0; i < MAX_PLAYERS; i++)
3746   {
3747     struct PlayerInfo *player = &stored_player[i];
3748
3749     player->index_nr = i;
3750     player->index_bit = (1 << i);
3751     player->element_nr = EL_PLAYER_1 + i;
3752
3753     player->present = FALSE;
3754     player->active = FALSE;
3755     player->mapped = FALSE;
3756
3757     player->killed = FALSE;
3758     player->reanimated = FALSE;
3759
3760     player->action = 0;
3761     player->effective_action = 0;
3762     player->programmed_action = 0;
3763
3764     player->score = 0;
3765     player->score_final = 0;
3766
3767     player->gems_still_needed = level.gems_needed;
3768     player->sokobanfields_still_needed = 0;
3769     player->lights_still_needed = 0;
3770     player->friends_still_needed = 0;
3771
3772     for (j = 0; j < MAX_NUM_KEYS; j++)
3773       player->key[j] = FALSE;
3774
3775     player->num_white_keys = 0;
3776
3777     player->dynabomb_count = 0;
3778     player->dynabomb_size = 1;
3779     player->dynabombs_left = 0;
3780     player->dynabomb_xl = FALSE;
3781
3782     player->MovDir = initial_move_dir;
3783     player->MovPos = 0;
3784     player->GfxPos = 0;
3785     player->GfxDir = initial_move_dir;
3786     player->GfxAction = ACTION_DEFAULT;
3787     player->Frame = 0;
3788     player->StepFrame = 0;
3789
3790     player->initial_element = player->element_nr;
3791     player->artwork_element =
3792       (level.use_artwork_element[i] ? level.artwork_element[i] :
3793        player->element_nr);
3794     player->use_murphy = FALSE;
3795
3796     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3797     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3798
3799     player->gravity = level.initial_player_gravity[i];
3800
3801     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3802
3803     player->actual_frame_counter = 0;
3804
3805     player->step_counter = 0;
3806
3807     player->last_move_dir = initial_move_dir;
3808
3809     player->is_active = FALSE;
3810
3811     player->is_waiting = FALSE;
3812     player->is_moving = FALSE;
3813     player->is_auto_moving = FALSE;
3814     player->is_digging = FALSE;
3815     player->is_snapping = FALSE;
3816     player->is_collecting = FALSE;
3817     player->is_pushing = FALSE;
3818     player->is_switching = FALSE;
3819     player->is_dropping = FALSE;
3820     player->is_dropping_pressed = FALSE;
3821
3822     player->is_bored = FALSE;
3823     player->is_sleeping = FALSE;
3824
3825     player->frame_counter_bored = -1;
3826     player->frame_counter_sleeping = -1;
3827
3828     player->anim_delay_counter = 0;
3829     player->post_delay_counter = 0;
3830
3831     player->dir_waiting = initial_move_dir;
3832     player->action_waiting = ACTION_DEFAULT;
3833     player->last_action_waiting = ACTION_DEFAULT;
3834     player->special_action_bored = ACTION_DEFAULT;
3835     player->special_action_sleeping = ACTION_DEFAULT;
3836
3837     player->switch_x = -1;
3838     player->switch_y = -1;
3839
3840     player->drop_x = -1;
3841     player->drop_y = -1;
3842
3843     player->show_envelope = 0;
3844
3845     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3846
3847     player->push_delay       = -1;      /* initialized when pushing starts */
3848     player->push_delay_value = game.initial_push_delay_value;
3849
3850     player->drop_delay = 0;
3851     player->drop_pressed_delay = 0;
3852
3853     player->last_jx = -1;
3854     player->last_jy = -1;
3855     player->jx = -1;
3856     player->jy = -1;
3857
3858     player->shield_normal_time_left = 0;
3859     player->shield_deadly_time_left = 0;
3860
3861     player->inventory_infinite_element = EL_UNDEFINED;
3862     player->inventory_size = 0;
3863
3864     if (level.use_initial_inventory[i])
3865     {
3866       for (j = 0; j < level.initial_inventory_size[i]; j++)
3867       {
3868         int element = level.initial_inventory_content[i][j];
3869         int collect_count = element_info[element].collect_count_initial;
3870         int k;
3871
3872         if (!IS_CUSTOM_ELEMENT(element))
3873           collect_count = 1;
3874
3875         if (collect_count == 0)
3876           player->inventory_infinite_element = element;
3877         else
3878           for (k = 0; k < collect_count; k++)
3879             if (player->inventory_size < MAX_INVENTORY_SIZE)
3880               player->inventory_element[player->inventory_size++] = element;
3881       }
3882     }
3883
3884     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885     SnapField(player, 0, 0);
3886
3887     player->LevelSolved = FALSE;
3888     player->GameOver = FALSE;
3889
3890     player->LevelSolved_GameWon = FALSE;
3891     player->LevelSolved_GameEnd = FALSE;
3892     player->LevelSolved_PanelOff = FALSE;
3893     player->LevelSolved_SaveTape = FALSE;
3894     player->LevelSolved_SaveScore = FALSE;
3895     player->LevelSolved_CountingTime = 0;
3896     player->LevelSolved_CountingScore = 0;
3897
3898     map_player_action[i] = i;
3899   }
3900
3901   network_player_action_received = FALSE;
3902
3903 #if defined(NETWORK_AVALIABLE)
3904   /* initial null action */
3905   if (network_playing)
3906     SendToServer_MovePlayer(MV_NONE);
3907 #endif
3908
3909   ZX = ZY = -1;
3910   ExitX = ExitY = -1;
3911
3912   FrameCounter = 0;
3913   TimeFrames = 0;
3914   TimePlayed = 0;
3915   TimeLeft = level.time;
3916   TapeTime = 0;
3917
3918   ScreenMovDir = MV_NONE;
3919   ScreenMovPos = 0;
3920   ScreenGfxPos = 0;
3921
3922   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3923
3924   AllPlayersGone = FALSE;
3925
3926   game.yamyam_content_nr = 0;
3927   game.robot_wheel_active = FALSE;
3928   game.magic_wall_active = FALSE;
3929   game.magic_wall_time_left = 0;
3930   game.light_time_left = 0;
3931   game.timegate_time_left = 0;
3932   game.switchgate_pos = 0;
3933   game.wind_direction = level.wind_direction_initial;
3934
3935 #if !USE_PLAYER_GRAVITY
3936   game.gravity = FALSE;
3937   game.explosions_delayed = TRUE;
3938 #endif
3939
3940   game.lenses_time_left = 0;
3941   game.magnify_time_left = 0;
3942
3943   game.ball_state = level.ball_state_initial;
3944   game.ball_content_nr = 0;
3945
3946   game.envelope_active = FALSE;
3947
3948   /* set focus to local player for network games, else to all players */
3949   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950   game.centered_player_nr_next = game.centered_player_nr;
3951   game.set_centered_player = FALSE;
3952
3953   if (network_playing && tape.recording)
3954   {
3955     /* store client dependent player focus when recording network games */
3956     tape.centered_player_nr_next = game.centered_player_nr_next;
3957     tape.set_centered_player = TRUE;
3958   }
3959
3960   for (i = 0; i < NUM_BELTS; i++)
3961   {
3962     game.belt_dir[i] = MV_NONE;
3963     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3964   }
3965
3966   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3968
3969   SCAN_PLAYFIELD(x, y)
3970   {
3971     Feld[x][y] = level.field[x][y];
3972     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973     ChangeDelay[x][y] = 0;
3974     ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976     CustomValue[x][y] = 0;              /* initialized in InitField() */
3977 #endif
3978     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3979     AmoebaNr[x][y] = 0;
3980     WasJustMoving[x][y] = 0;
3981     WasJustFalling[x][y] = 0;
3982     CheckCollision[x][y] = 0;
3983     CheckImpact[x][y] = 0;
3984     Stop[x][y] = FALSE;
3985     Pushed[x][y] = FALSE;
3986
3987     ChangeCount[x][y] = 0;
3988     ChangeEvent[x][y] = -1;
3989
3990     ExplodePhase[x][y] = 0;
3991     ExplodeDelay[x][y] = 0;
3992     ExplodeField[x][y] = EX_TYPE_NONE;
3993
3994     RunnerVisit[x][y] = 0;
3995     PlayerVisit[x][y] = 0;
3996
3997     GfxFrame[x][y] = 0;
3998     GfxRandom[x][y] = INIT_GFX_RANDOM();
3999     GfxElement[x][y] = EL_UNDEFINED;
4000     GfxAction[x][y] = ACTION_DEFAULT;
4001     GfxDir[x][y] = MV_NONE;
4002     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4003   }
4004
4005   SCAN_PLAYFIELD(x, y)
4006   {
4007     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4008       emulate_bd = FALSE;
4009     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4010       emulate_sb = FALSE;
4011     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4012       emulate_sp = FALSE;
4013
4014     InitField(x, y, TRUE);
4015
4016     ResetGfxAnimation(x, y);
4017   }
4018
4019   InitBeltMovement();
4020
4021   for (i = 0; i < MAX_PLAYERS; i++)
4022   {
4023     struct PlayerInfo *player = &stored_player[i];
4024
4025     /* set number of special actions for bored and sleeping animation */
4026     player->num_special_action_bored =
4027       get_num_special_action(player->artwork_element,
4028                              ACTION_BORING_1, ACTION_BORING_LAST);
4029     player->num_special_action_sleeping =
4030       get_num_special_action(player->artwork_element,
4031                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4032   }
4033
4034   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035                     emulate_sb ? EMU_SOKOBAN :
4036                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4037
4038 #if USE_NEW_ALL_SLIPPERY
4039   /* initialize type of slippery elements */
4040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4041   {
4042     if (!IS_CUSTOM_ELEMENT(i))
4043     {
4044       /* default: elements slip down either to the left or right randomly */
4045       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4046
4047       /* SP style elements prefer to slip down on the left side */
4048       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4050
4051       /* BD style elements prefer to slip down on the left side */
4052       if (game.emulation == EMU_BOULDERDASH)
4053         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4054     }
4055   }
4056 #endif
4057
4058   /* initialize explosion and ignition delay */
4059   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4060   {
4061     if (!IS_CUSTOM_ELEMENT(i))
4062     {
4063       int num_phase = 8;
4064       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067       int last_phase = (num_phase + 1) * delay;
4068       int half_phase = (num_phase / 2) * delay;
4069
4070       element_info[i].explosion_delay = last_phase - 1;
4071       element_info[i].ignition_delay = half_phase;
4072
4073       if (i == EL_BLACK_ORB)
4074         element_info[i].ignition_delay = 1;
4075     }
4076
4077 #if 0
4078     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4079       element_info[i].explosion_delay = 1;
4080
4081     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4082       element_info[i].ignition_delay = 1;
4083 #endif
4084   }
4085
4086   /* correct non-moving belts to start moving left */
4087   for (i = 0; i < NUM_BELTS; i++)
4088     if (game.belt_dir[i] == MV_NONE)
4089       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4090
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093   /* choose default local player */
4094   local_player = &stored_player[0];
4095
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097     stored_player[i].connected = FALSE;
4098
4099   local_player->connected = TRUE;
4100   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4101
4102   if (tape.playing)
4103   {
4104     /* try to guess locally connected team mode players (needed for correct
4105        assignment of player figures from level to locally playing players) */
4106
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (tape.player_participates[i])
4109         stored_player[i].connected = TRUE;
4110   }
4111   else if (setup.team_mode && !options.network)
4112   {
4113     /* try to guess locally connected team mode players (needed for correct
4114        assignment of player figures from level to locally playing players) */
4115
4116     for (i = 0; i < MAX_PLAYERS; i++)
4117       if (setup.input[i].use_joystick ||
4118           setup.input[i].key.left != KSYM_UNDEFINED)
4119         stored_player[i].connected = TRUE;
4120   }
4121
4122 #if 0
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124     printf("::: player %d: %s\n", i,
4125            (stored_player[i].connected ? "connected" : "not connected"));
4126
4127   for (i = 0; i < MAX_PLAYERS; i++)
4128     printf("::: player %d: %s\n", i,
4129            (stored_player[i].present ? "present" : "not present"));
4130 #endif
4131
4132   /* check if any connected player was not found in playfield */
4133   for (i = 0; i < MAX_PLAYERS; i++)
4134   {
4135     struct PlayerInfo *player = &stored_player[i];
4136
4137     if (player->connected && !player->present)
4138     {
4139       struct PlayerInfo *field_player = NULL;
4140
4141 #if 0
4142       printf("::: looking for field player for player %d ...\n", i);
4143 #endif
4144
4145       /* assign first free player found that is present in the playfield */
4146
4147       /* first try: look for unmapped playfield player that is not connected */
4148       if (field_player == NULL)
4149         for (j = 0; j < MAX_PLAYERS; j++)
4150           if (stored_player[j].present &&
4151               !stored_player[j].mapped &&
4152               !stored_player[j].connected)
4153             field_player = &stored_player[j];
4154
4155       /* second try: look for *any* unmapped playfield player */
4156       if (field_player == NULL)
4157         for (j = 0; j < MAX_PLAYERS; j++)
4158           if (stored_player[j].present &&
4159               !stored_player[j].mapped)
4160             field_player = &stored_player[j];
4161
4162       if (field_player != NULL)
4163       {
4164         int jx = field_player->jx, jy = field_player->jy;
4165
4166 #if 0
4167         printf("::: found player figure %d\n", field_player->index_nr);
4168 #endif
4169
4170         player->present = FALSE;
4171         player->active = FALSE;
4172
4173         field_player->present = TRUE;
4174         field_player->active = TRUE;
4175
4176         /*
4177         player->initial_element = field_player->initial_element;
4178         player->artwork_element = field_player->artwork_element;
4179
4180         player->block_last_field       = field_player->block_last_field;
4181         player->block_delay_adjustment = field_player->block_delay_adjustment;
4182         */
4183
4184         StorePlayer[jx][jy] = field_player->element_nr;
4185
4186         field_player->jx = field_player->last_jx = jx;
4187         field_player->jy = field_player->last_jy = jy;
4188
4189         if (local_player == player)
4190           local_player = field_player;
4191
4192         map_player_action[field_player->index_nr] = i;
4193
4194         field_player->mapped = TRUE;
4195
4196 #if 0
4197         printf("::: map_player_action[%d] == %d\n",
4198                field_player->index_nr, i);
4199 #endif
4200       }
4201     }
4202
4203     if (player->connected && player->present)
4204       player->mapped = TRUE;
4205   }
4206
4207 #else
4208
4209   /* check if any connected player was not found in playfield */
4210   for (i = 0; i < MAX_PLAYERS; i++)
4211   {
4212     struct PlayerInfo *player = &stored_player[i];
4213
4214     if (player->connected && !player->present)
4215     {
4216       for (j = 0; j < MAX_PLAYERS; j++)
4217       {
4218         struct PlayerInfo *field_player = &stored_player[j];
4219         int jx = field_player->jx, jy = field_player->jy;
4220
4221         /* assign first free player found that is present in the playfield */
4222         if (field_player->present && !field_player->connected)
4223         {
4224           player->present = TRUE;
4225           player->active = TRUE;
4226
4227           field_player->present = FALSE;
4228           field_player->active = FALSE;
4229
4230           player->initial_element = field_player->initial_element;
4231           player->artwork_element = field_player->artwork_element;
4232
4233           player->block_last_field       = field_player->block_last_field;
4234           player->block_delay_adjustment = field_player->block_delay_adjustment;
4235
4236           StorePlayer[jx][jy] = player->element_nr;
4237
4238           player->jx = player->last_jx = jx;
4239           player->jy = player->last_jy = jy;
4240
4241           break;
4242         }
4243       }
4244     }
4245   }
4246 #endif
4247
4248 #if 0
4249   printf("::: local_player->present == %d\n", local_player->present);
4250 #endif
4251
4252   if (tape.playing)
4253   {
4254     /* when playing a tape, eliminate all players who do not participate */
4255
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257     for (i = 0; i < MAX_PLAYERS; i++)
4258     {
4259       if (stored_player[i].active &&
4260           !tape.player_participates[map_player_action[i]])
4261       {
4262         struct PlayerInfo *player = &stored_player[i];
4263         int jx = player->jx, jy = player->jy;
4264
4265         player->active = FALSE;
4266         StorePlayer[jx][jy] = 0;
4267         Feld[jx][jy] = EL_EMPTY;
4268       }
4269     }
4270 #else
4271     for (i = 0; i < MAX_PLAYERS; i++)
4272     {
4273       if (stored_player[i].active &&
4274           !tape.player_participates[i])
4275       {
4276         struct PlayerInfo *player = &stored_player[i];
4277         int jx = player->jx, jy = player->jy;
4278
4279         player->active = FALSE;
4280         StorePlayer[jx][jy] = 0;
4281         Feld[jx][jy] = EL_EMPTY;
4282       }
4283     }
4284 #endif
4285   }
4286   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4287   {
4288     /* when in single player mode, eliminate all but the first active player */
4289
4290     for (i = 0; i < MAX_PLAYERS; i++)
4291     {
4292       if (stored_player[i].active)
4293       {
4294         for (j = i + 1; j < MAX_PLAYERS; j++)
4295         {
4296           if (stored_player[j].active)
4297           {
4298             struct PlayerInfo *player = &stored_player[j];
4299             int jx = player->jx, jy = player->jy;
4300
4301             player->active = FALSE;
4302             player->present = FALSE;
4303
4304             StorePlayer[jx][jy] = 0;
4305             Feld[jx][jy] = EL_EMPTY;
4306           }
4307         }
4308       }
4309     }
4310   }
4311
4312   /* when recording the game, store which players take part in the game */
4313   if (tape.recording)
4314   {
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316     for (i = 0; i < MAX_PLAYERS; i++)
4317       if (stored_player[i].connected)
4318         tape.player_participates[i] = TRUE;
4319 #else
4320     for (i = 0; i < MAX_PLAYERS; i++)
4321       if (stored_player[i].active)
4322         tape.player_participates[i] = TRUE;
4323 #endif
4324   }
4325
4326   if (options.debug)
4327   {
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329     {
4330       struct PlayerInfo *player = &stored_player[i];
4331
4332       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4333              i+1,
4334              player->present,
4335              player->connected,
4336              player->active);
4337       if (local_player == player)
4338         printf("Player  %d is local player.\n", i+1);
4339     }
4340   }
4341
4342   if (BorderElement == EL_EMPTY)
4343   {
4344     SBX_Left = 0;
4345     SBX_Right = lev_fieldx - SCR_FIELDX;
4346     SBY_Upper = 0;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY;
4348   }
4349   else
4350   {
4351     SBX_Left = -1;
4352     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4353     SBY_Upper = -1;
4354     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4355   }
4356
4357 #if NEW_TILESIZE
4358 #if 1
4359   if (TILESIZE_VAR < TILESIZE && EVEN(SCR_FIELDX))
4360   {
4361     // SBX_Left--;
4362     SBX_Right++;
4363   }
4364 #endif
4365 #endif
4366
4367   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4368     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4369
4370   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4371     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4372
4373   /* if local player not found, look for custom element that might create
4374      the player (make some assumptions about the right custom element) */
4375   if (!local_player->present)
4376   {
4377     int start_x = 0, start_y = 0;
4378     int found_rating = 0;
4379     int found_element = EL_UNDEFINED;
4380     int player_nr = local_player->index_nr;
4381
4382     SCAN_PLAYFIELD(x, y)
4383     {
4384       int element = Feld[x][y];
4385       int content;
4386       int xx, yy;
4387       boolean is_player;
4388
4389       if (level.use_start_element[player_nr] &&
4390           level.start_element[player_nr] == element &&
4391           found_rating < 4)
4392       {
4393         start_x = x;
4394         start_y = y;
4395
4396         found_rating = 4;
4397         found_element = element;
4398       }
4399
4400       if (!IS_CUSTOM_ELEMENT(element))
4401         continue;
4402
4403       if (CAN_CHANGE(element))
4404       {
4405         for (i = 0; i < element_info[element].num_change_pages; i++)
4406         {
4407           /* check for player created from custom element as single target */
4408           content = element_info[element].change_page[i].target_element;
4409           is_player = ELEM_IS_PLAYER(content);
4410
4411           if (is_player && (found_rating < 3 ||
4412                             (found_rating == 3 && element < found_element)))
4413           {
4414             start_x = x;
4415             start_y = y;
4416
4417             found_rating = 3;
4418             found_element = element;
4419           }
4420         }
4421       }
4422
4423       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4424       {
4425         /* check for player created from custom element as explosion content */
4426         content = element_info[element].content.e[xx][yy];
4427         is_player = ELEM_IS_PLAYER(content);
4428
4429         if (is_player && (found_rating < 2 ||
4430                           (found_rating == 2 && element < found_element)))
4431         {
4432           start_x = x + xx - 1;
4433           start_y = y + yy - 1;
4434
4435           found_rating = 2;
4436           found_element = element;
4437         }
4438
4439         if (!CAN_CHANGE(element))
4440           continue;
4441
4442         for (i = 0; i < element_info[element].num_change_pages; i++)
4443         {
4444           /* check for player created from custom element as extended target */
4445           content =
4446             element_info[element].change_page[i].target_content.e[xx][yy];
4447
4448           is_player = ELEM_IS_PLAYER(content);
4449
4450           if (is_player && (found_rating < 1 ||
4451                             (found_rating == 1 && element < found_element)))
4452           {
4453             start_x = x + xx - 1;
4454             start_y = y + yy - 1;
4455
4456             found_rating = 1;
4457             found_element = element;
4458           }
4459         }
4460       }
4461     }
4462
4463     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4464                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4465                 start_x - MIDPOSX);
4466
4467     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4468                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4469                 start_y - MIDPOSY);
4470   }
4471   else
4472   {
4473     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4474                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4475                 local_player->jx - MIDPOSX);
4476
4477     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4478                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4479                 local_player->jy - MIDPOSY);
4480   }
4481
4482 #if 0
4483   /* do not use PLAYING mask for fading out from main screen */
4484   game_status = GAME_MODE_MAIN;
4485 #endif
4486
4487   StopAnimation();
4488
4489   if (!game.restart_level)
4490     CloseDoor(DOOR_CLOSE_1);
4491
4492 #if 1
4493   if (level_editor_test_game)
4494     FadeSkipNextFadeIn();
4495   else
4496     FadeSetEnterScreen();
4497 #else
4498   if (level_editor_test_game)
4499     fading = fading_none;
4500   else
4501     fading = menu.destination;
4502 #endif
4503
4504 #if 1
4505   FadeOut(REDRAW_FIELD);
4506 #else
4507   if (do_fading)
4508     FadeOut(REDRAW_FIELD);
4509 #endif
4510
4511 #if 0
4512   game_status = GAME_MODE_PLAYING;
4513 #endif
4514
4515   /* !!! FIX THIS (START) !!! */
4516   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4517   {
4518     InitGameEngine_EM();
4519
4520     /* blit playfield from scroll buffer to normal back buffer for fading in */
4521     BlitScreenToBitmap_EM(backbuffer);
4522   }
4523   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4524   {
4525     InitGameEngine_SP();
4526
4527     /* blit playfield from scroll buffer to normal back buffer for fading in */
4528     BlitScreenToBitmap_SP(backbuffer);
4529   }
4530   else
4531   {
4532     DrawLevel();
4533     DrawAllPlayers();
4534
4535     /* after drawing the level, correct some elements */
4536     if (game.timegate_time_left == 0)
4537       CloseAllOpenTimegates();
4538
4539     /* blit playfield from scroll buffer to normal back buffer for fading in */
4540     if (setup.soft_scrolling)
4541       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4542
4543     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4544   }
4545   /* !!! FIX THIS (END) !!! */
4546
4547 #if 1
4548   FadeIn(REDRAW_FIELD);
4549 #else
4550   if (do_fading)
4551     FadeIn(REDRAW_FIELD);
4552
4553   BackToFront();
4554 #endif
4555
4556   if (!game.restart_level)
4557   {
4558     /* copy default game door content to main double buffer */
4559 #if 1
4560 #if 1
4561     /* !!! CHECK AGAIN !!! */
4562     SetPanelBackground();
4563     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4564     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4565 #else
4566     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4567
4568     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4569     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4570     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4571                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4572 #endif
4573 #else
4574     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4575                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4576 #endif
4577   }
4578
4579   SetPanelBackground();
4580   SetDrawBackgroundMask(REDRAW_DOOR_1);
4581
4582 #if 1
4583   UpdateAndDisplayGameControlValues();
4584 #else
4585   UpdateGameDoorValues();
4586   DrawGameDoorValues();
4587 #endif
4588
4589   if (!game.restart_level)
4590   {
4591     UnmapGameButtons();
4592     UnmapTapeButtons();
4593     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4594     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4595     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4596     MapGameButtons();
4597     MapTapeButtons();
4598
4599     /* copy actual game door content to door double buffer for OpenDoor() */
4600     BlitBitmap(drawto, bitmap_db_door,
4601                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4602
4603     OpenDoor(DOOR_OPEN_ALL);
4604
4605     PlaySound(SND_GAME_STARTING);
4606
4607     if (setup.sound_music)
4608       PlayLevelMusic();
4609
4610     KeyboardAutoRepeatOffUnlessAutoplay();
4611
4612     if (options.debug)
4613     {
4614       for (i = 0; i < MAX_PLAYERS; i++)
4615         printf("Player %d %sactive.\n",
4616                i + 1, (stored_player[i].active ? "" : "not "));
4617     }
4618   }
4619
4620 #if 1
4621   UnmapAllGadgets();
4622
4623   MapGameButtons();
4624   MapTapeButtons();
4625 #endif
4626
4627   game.restart_level = FALSE;
4628 }
4629
4630 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4631 {
4632   /* this is used for non-R'n'D game engines to update certain engine values */
4633
4634   /* needed to determine if sounds are played within the visible screen area */
4635   scroll_x = actual_scroll_x;
4636   scroll_y = actual_scroll_y;
4637 }
4638
4639 void InitMovDir(int x, int y)
4640 {
4641   int i, element = Feld[x][y];
4642   static int xy[4][2] =
4643   {
4644     {  0, +1 },
4645     { +1,  0 },
4646     {  0, -1 },
4647     { -1,  0 }
4648   };
4649   static int direction[3][4] =
4650   {
4651     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4652     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4653     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4654   };
4655
4656   switch (element)
4657   {
4658     case EL_BUG_RIGHT:
4659     case EL_BUG_UP:
4660     case EL_BUG_LEFT:
4661     case EL_BUG_DOWN:
4662       Feld[x][y] = EL_BUG;
4663       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4664       break;
4665
4666     case EL_SPACESHIP_RIGHT:
4667     case EL_SPACESHIP_UP:
4668     case EL_SPACESHIP_LEFT:
4669     case EL_SPACESHIP_DOWN:
4670       Feld[x][y] = EL_SPACESHIP;
4671       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4672       break;
4673
4674     case EL_BD_BUTTERFLY_RIGHT:
4675     case EL_BD_BUTTERFLY_UP:
4676     case EL_BD_BUTTERFLY_LEFT:
4677     case EL_BD_BUTTERFLY_DOWN:
4678       Feld[x][y] = EL_BD_BUTTERFLY;
4679       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4680       break;
4681
4682     case EL_BD_FIREFLY_RIGHT:
4683     case EL_BD_FIREFLY_UP:
4684     case EL_BD_FIREFLY_LEFT:
4685     case EL_BD_FIREFLY_DOWN:
4686       Feld[x][y] = EL_BD_FIREFLY;
4687       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4688       break;
4689
4690     case EL_PACMAN_RIGHT:
4691     case EL_PACMAN_UP:
4692     case EL_PACMAN_LEFT:
4693     case EL_PACMAN_DOWN:
4694       Feld[x][y] = EL_PACMAN;
4695       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4696       break;
4697
4698     case EL_YAMYAM_LEFT:
4699     case EL_YAMYAM_RIGHT:
4700     case EL_YAMYAM_UP:
4701     case EL_YAMYAM_DOWN:
4702       Feld[x][y] = EL_YAMYAM;
4703       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4704       break;
4705
4706     case EL_SP_SNIKSNAK:
4707       MovDir[x][y] = MV_UP;
4708       break;
4709
4710     case EL_SP_ELECTRON:
4711       MovDir[x][y] = MV_LEFT;
4712       break;
4713
4714     case EL_MOLE_LEFT:
4715     case EL_MOLE_RIGHT:
4716     case EL_MOLE_UP:
4717     case EL_MOLE_DOWN:
4718       Feld[x][y] = EL_MOLE;
4719       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4720       break;
4721
4722     default:
4723       if (IS_CUSTOM_ELEMENT(element))
4724       {
4725         struct ElementInfo *ei = &element_info[element];
4726         int move_direction_initial = ei->move_direction_initial;
4727         int move_pattern = ei->move_pattern;
4728
4729         if (move_direction_initial == MV_START_PREVIOUS)
4730         {
4731           if (MovDir[x][y] != MV_NONE)
4732             return;
4733
4734           move_direction_initial = MV_START_AUTOMATIC;
4735         }
4736
4737         if (move_direction_initial == MV_START_RANDOM)
4738           MovDir[x][y] = 1 << RND(4);
4739         else if (move_direction_initial & MV_ANY_DIRECTION)
4740           MovDir[x][y] = move_direction_initial;
4741         else if (move_pattern == MV_ALL_DIRECTIONS ||
4742                  move_pattern == MV_TURNING_LEFT ||
4743                  move_pattern == MV_TURNING_RIGHT ||
4744                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4745                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4746                  move_pattern == MV_TURNING_RANDOM)
4747           MovDir[x][y] = 1 << RND(4);
4748         else if (move_pattern == MV_HORIZONTAL)
4749           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4750         else if (move_pattern == MV_VERTICAL)
4751           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4752         else if (move_pattern & MV_ANY_DIRECTION)
4753           MovDir[x][y] = element_info[element].move_pattern;
4754         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4755                  move_pattern == MV_ALONG_RIGHT_SIDE)
4756         {
4757           /* use random direction as default start direction */
4758           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4759             MovDir[x][y] = 1 << RND(4);
4760
4761           for (i = 0; i < NUM_DIRECTIONS; i++)
4762           {
4763             int x1 = x + xy[i][0];
4764             int y1 = y + xy[i][1];
4765
4766             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4767             {
4768               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4769                 MovDir[x][y] = direction[0][i];
4770               else
4771                 MovDir[x][y] = direction[1][i];
4772
4773               break;
4774             }
4775           }
4776         }                
4777       }
4778       else
4779       {
4780         MovDir[x][y] = 1 << RND(4);
4781
4782         if (element != EL_BUG &&
4783             element != EL_SPACESHIP &&
4784             element != EL_BD_BUTTERFLY &&
4785             element != EL_BD_FIREFLY)
4786           break;
4787
4788         for (i = 0; i < NUM_DIRECTIONS; i++)
4789         {
4790           int x1 = x + xy[i][0];
4791           int y1 = y + xy[i][1];
4792
4793           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4794           {
4795             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4796             {
4797               MovDir[x][y] = direction[0][i];
4798               break;
4799             }
4800             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4801                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4802             {
4803               MovDir[x][y] = direction[1][i];
4804               break;
4805             }
4806           }
4807         }
4808       }
4809       break;
4810   }
4811
4812   GfxDir[x][y] = MovDir[x][y];
4813 }
4814
4815 void InitAmoebaNr(int x, int y)
4816 {
4817   int i;
4818   int group_nr = AmoebeNachbarNr(x, y);
4819
4820   if (group_nr == 0)
4821   {
4822     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4823     {
4824       if (AmoebaCnt[i] == 0)
4825       {
4826         group_nr = i;
4827         break;
4828       }
4829     }
4830   }
4831
4832   AmoebaNr[x][y] = group_nr;
4833   AmoebaCnt[group_nr]++;
4834   AmoebaCnt2[group_nr]++;
4835 }
4836
4837 static void PlayerWins(struct PlayerInfo *player)
4838 {
4839   player->LevelSolved = TRUE;
4840   player->GameOver = TRUE;
4841
4842   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4843                          level.native_em_level->lev->score : player->score);
4844
4845   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4846   player->LevelSolved_CountingScore = player->score_final;
4847 }
4848
4849 void GameWon()
4850 {
4851   static int time, time_final;
4852   static int score, score_final;
4853   static int game_over_delay_1 = 0;
4854   static int game_over_delay_2 = 0;
4855   int game_over_delay_value_1 = 50;
4856   int game_over_delay_value_2 = 50;
4857
4858   if (!local_player->LevelSolved_GameWon)
4859   {
4860     int i;
4861
4862     /* do not start end game actions before the player stops moving (to exit) */
4863     if (local_player->MovPos)
4864       return;
4865
4866     local_player->LevelSolved_GameWon = TRUE;
4867     local_player->LevelSolved_SaveTape = tape.recording;
4868     local_player->LevelSolved_SaveScore = !tape.playing;
4869
4870     if (tape.auto_play)         /* tape might already be stopped here */
4871       tape.auto_play_level_solved = TRUE;
4872
4873 #if 1
4874     TapeStop();
4875 #endif
4876
4877     game_over_delay_1 = game_over_delay_value_1;
4878     game_over_delay_2 = game_over_delay_value_2;
4879
4880     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4881     score = score_final = local_player->score_final;
4882
4883     if (TimeLeft > 0)
4884     {
4885       time_final = 0;
4886       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4887     }
4888     else if (level.time == 0 && TimePlayed < 999)
4889     {
4890       time_final = 999;
4891       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4892     }
4893
4894     local_player->score_final = score_final;
4895
4896     if (level_editor_test_game)
4897     {
4898       time = time_final;
4899       score = score_final;
4900
4901 #if 1
4902       local_player->LevelSolved_CountingTime = time;
4903       local_player->LevelSolved_CountingScore = score;
4904
4905       game_panel_controls[GAME_PANEL_TIME].value = time;
4906       game_panel_controls[GAME_PANEL_SCORE].value = score;
4907
4908       DisplayGameControlValues();
4909 #else
4910       DrawGameValue_Time(time);
4911       DrawGameValue_Score(score);
4912 #endif
4913     }
4914
4915     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4916     {
4917       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4918       {
4919         /* close exit door after last player */
4920         if ((AllPlayersGone &&
4921              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4922               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4923               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4924             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4925             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4926         {
4927           int element = Feld[ExitX][ExitY];
4928
4929 #if 0
4930           if (element == EL_EM_EXIT_OPEN ||
4931               element == EL_EM_STEEL_EXIT_OPEN)
4932           {
4933             Bang(ExitX, ExitY);
4934           }
4935           else
4936 #endif
4937           {
4938             Feld[ExitX][ExitY] =
4939               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4940                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4941                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4942                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4943                EL_EM_STEEL_EXIT_CLOSING);
4944
4945             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4946           }
4947         }
4948
4949         /* player disappears */
4950         DrawLevelField(ExitX, ExitY);
4951       }
4952
4953       for (i = 0; i < MAX_PLAYERS; i++)
4954       {
4955         struct PlayerInfo *player = &stored_player[i];
4956
4957         if (player->present)
4958         {
4959           RemovePlayer(player);
4960
4961           /* player disappears */
4962           DrawLevelField(player->jx, player->jy);
4963         }
4964       }
4965     }
4966
4967     PlaySound(SND_GAME_WINNING);
4968   }
4969
4970   if (game_over_delay_1 > 0)
4971   {
4972     game_over_delay_1--;
4973
4974     return;
4975   }
4976
4977   if (time != time_final)
4978   {
4979     int time_to_go = ABS(time_final - time);
4980     int time_count_dir = (time < time_final ? +1 : -1);
4981     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4982
4983     time  += time_count_steps * time_count_dir;
4984     score += time_count_steps * level.score[SC_TIME_BONUS];
4985
4986 #if 1
4987     local_player->LevelSolved_CountingTime = time;
4988     local_player->LevelSolved_CountingScore = score;
4989
4990     game_panel_controls[GAME_PANEL_TIME].value = time;
4991     game_panel_controls[GAME_PANEL_SCORE].value = score;
4992
4993     DisplayGameControlValues();
4994 #else
4995     DrawGameValue_Time(time);
4996     DrawGameValue_Score(score);
4997 #endif
4998
4999     if (time == time_final)
5000       StopSound(SND_GAME_LEVELTIME_BONUS);
5001     else if (setup.sound_loops)
5002       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5003     else
5004       PlaySound(SND_GAME_LEVELTIME_BONUS);
5005
5006     return;
5007   }
5008
5009   local_player->LevelSolved_PanelOff = TRUE;
5010
5011   if (game_over_delay_2 > 0)
5012   {
5013     game_over_delay_2--;
5014
5015     return;
5016   }
5017
5018 #if 1
5019   GameEnd();
5020 #endif
5021 }
5022
5023 void GameEnd()
5024 {
5025   int hi_pos;
5026   boolean raise_level = FALSE;
5027
5028   local_player->LevelSolved_GameEnd = TRUE;
5029
5030   CloseDoor(DOOR_CLOSE_1);
5031
5032   if (local_player->LevelSolved_SaveTape)
5033   {
5034 #if 0
5035     TapeStop();
5036 #endif
5037
5038 #if 1
5039     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5040 #else
5041     SaveTape(tape.level_nr);            /* ask to save tape */
5042 #endif
5043   }
5044
5045   if (level_editor_test_game)
5046   {
5047     game_status = GAME_MODE_MAIN;
5048
5049 #if 1
5050     DrawAndFadeInMainMenu(REDRAW_FIELD);
5051 #else
5052     DrawMainMenu();
5053 #endif
5054
5055     return;
5056   }
5057
5058   if (!local_player->LevelSolved_SaveScore)
5059   {
5060 #if 1
5061     FadeOut(REDRAW_FIELD);
5062 #endif
5063
5064     game_status = GAME_MODE_MAIN;
5065
5066     DrawAndFadeInMainMenu(REDRAW_FIELD);
5067
5068     return;
5069   }
5070
5071   if (level_nr == leveldir_current->handicap_level)
5072   {
5073     leveldir_current->handicap_level++;
5074     SaveLevelSetup_SeriesInfo();
5075   }
5076
5077   if (level_nr < leveldir_current->last_level)
5078     raise_level = TRUE;                 /* advance to next level */
5079
5080   if ((hi_pos = NewHiScore()) >= 0) 
5081   {
5082     game_status = GAME_MODE_SCORES;
5083
5084     DrawHallOfFame(hi_pos);
5085
5086     if (raise_level)
5087     {
5088       level_nr++;
5089       TapeErase();
5090     }
5091   }
5092   else
5093   {
5094 #if 1
5095     FadeOut(REDRAW_FIELD);
5096 #endif
5097
5098     game_status = GAME_MODE_MAIN;
5099
5100     if (raise_level)
5101     {
5102       level_nr++;
5103       TapeErase();
5104     }
5105
5106     DrawAndFadeInMainMenu(REDRAW_FIELD);
5107   }
5108 }
5109
5110 int NewHiScore()
5111 {
5112   int k, l;
5113   int position = -1;
5114
5115   LoadScore(level_nr);
5116
5117   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5118       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5119     return -1;
5120
5121   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5122   {
5123     if (local_player->score_final > highscore[k].Score)
5124     {
5125       /* player has made it to the hall of fame */
5126
5127       if (k < MAX_SCORE_ENTRIES - 1)
5128       {
5129         int m = MAX_SCORE_ENTRIES - 1;
5130
5131 #ifdef ONE_PER_NAME
5132         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5133           if (strEqual(setup.player_name, highscore[l].Name))
5134             m = l;
5135         if (m == k)     /* player's new highscore overwrites his old one */
5136           goto put_into_list;
5137 #endif
5138
5139         for (l = m; l > k; l--)
5140         {
5141           strcpy(highscore[l].Name, highscore[l - 1].Name);
5142           highscore[l].Score = highscore[l - 1].Score;
5143         }
5144       }
5145
5146 #ifdef ONE_PER_NAME
5147       put_into_list:
5148 #endif
5149       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5150       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5151       highscore[k].Score = local_player->score_final; 
5152       position = k;
5153       break;
5154     }
5155
5156 #ifdef ONE_PER_NAME
5157     else if (!strncmp(setup.player_name, highscore[k].Name,
5158                       MAX_PLAYER_NAME_LEN))
5159       break;    /* player already there with a higher score */
5160 #endif
5161
5162   }
5163
5164   if (position >= 0) 
5165     SaveScore(level_nr);
5166
5167   return position;
5168 }
5169
5170 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5171 {
5172   int element = Feld[x][y];
5173   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5174   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5175   int horiz_move = (dx != 0);
5176   int sign = (horiz_move ? dx : dy);
5177   int step = sign * element_info[element].move_stepsize;
5178
5179   /* special values for move stepsize for spring and things on conveyor belt */
5180   if (horiz_move)
5181   {
5182     if (CAN_FALL(element) &&
5183         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5184       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5185     else if (element == EL_SPRING)
5186       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5187   }
5188
5189   return step;
5190 }
5191
5192 inline static int getElementMoveStepsize(int x, int y)
5193 {
5194   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5195 }
5196
5197 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5198 {
5199   if (player->GfxAction != action || player->GfxDir != dir)
5200   {
5201 #if 0
5202     printf("Player frame reset! (%d => %d, %d => %d)\n",
5203            player->GfxAction, action, player->GfxDir, dir);
5204 #endif
5205
5206     player->GfxAction = action;
5207     player->GfxDir = dir;
5208     player->Frame = 0;
5209     player->StepFrame = 0;
5210   }
5211 }
5212
5213 #if USE_GFX_RESET_GFX_ANIMATION
5214 static void ResetGfxFrame(int x, int y, boolean redraw)
5215 {
5216   int element = Feld[x][y];
5217   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5218   int last_gfx_frame = GfxFrame[x][y];
5219
5220   if (graphic_info[graphic].anim_global_sync)
5221     GfxFrame[x][y] = FrameCounter;
5222   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5223     GfxFrame[x][y] = CustomValue[x][y];
5224   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5225     GfxFrame[x][y] = element_info[element].collect_score;
5226   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5227     GfxFrame[x][y] = ChangeDelay[x][y];
5228
5229   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5230     DrawLevelGraphicAnimation(x, y, graphic);
5231 }
5232 #endif
5233
5234 static void ResetGfxAnimation(int x, int y)
5235 {
5236   GfxAction[x][y] = ACTION_DEFAULT;
5237   GfxDir[x][y] = MovDir[x][y];
5238   GfxFrame[x][y] = 0;
5239
5240 #if USE_GFX_RESET_GFX_ANIMATION
5241   ResetGfxFrame(x, y, FALSE);
5242 #endif
5243 }
5244
5245 static void ResetRandomAnimationValue(int x, int y)
5246 {
5247   GfxRandom[x][y] = INIT_GFX_RANDOM();
5248 }
5249
5250 void InitMovingField(int x, int y, int direction)
5251 {
5252   int element = Feld[x][y];
5253   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5254   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5255   int newx = x + dx;
5256   int newy = y + dy;
5257   boolean is_moving_before, is_moving_after;
5258 #if 0
5259   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5260 #endif
5261
5262   /* check if element was/is moving or being moved before/after mode change */
5263 #if 1
5264 #if 1
5265   is_moving_before = (WasJustMoving[x][y] != 0);
5266 #else
5267   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5268   is_moving_before = WasJustMoving[x][y];
5269 #endif
5270 #else
5271   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5272 #endif
5273   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5274
5275   /* reset animation only for moving elements which change direction of moving
5276      or which just started or stopped moving
5277      (else CEs with property "can move" / "not moving" are reset each frame) */
5278 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5279 #if 1
5280   if (is_moving_before != is_moving_after ||
5281       direction != MovDir[x][y])
5282     ResetGfxAnimation(x, y);
5283 #else
5284   if ((is_moving_before || is_moving_after) && !continues_moving)
5285     ResetGfxAnimation(x, y);
5286 #endif
5287 #else
5288   if (!continues_moving)
5289     ResetGfxAnimation(x, y);
5290 #endif
5291
5292   MovDir[x][y] = direction;
5293   GfxDir[x][y] = direction;
5294
5295 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5296   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5297                      direction == MV_DOWN && CAN_FALL(element) ?
5298                      ACTION_FALLING : ACTION_MOVING);
5299 #else
5300   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5301                      ACTION_FALLING : ACTION_MOVING);
5302 #endif
5303
5304   /* this is needed for CEs with property "can move" / "not moving" */
5305
5306   if (is_moving_after)
5307   {
5308     if (Feld[newx][newy] == EL_EMPTY)
5309       Feld[newx][newy] = EL_BLOCKED;
5310
5311     MovDir[newx][newy] = MovDir[x][y];
5312
5313 #if USE_NEW_CUSTOM_VALUE
5314     CustomValue[newx][newy] = CustomValue[x][y];
5315 #endif
5316
5317     GfxFrame[newx][newy] = GfxFrame[x][y];
5318     GfxRandom[newx][newy] = GfxRandom[x][y];
5319     GfxAction[newx][newy] = GfxAction[x][y];
5320     GfxDir[newx][newy] = GfxDir[x][y];
5321   }
5322 }
5323
5324 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5325 {
5326   int direction = MovDir[x][y];
5327   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5328   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5329
5330   *goes_to_x = newx;
5331   *goes_to_y = newy;
5332 }
5333
5334 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5335 {
5336   int oldx = x, oldy = y;
5337   int direction = MovDir[x][y];
5338
5339   if (direction == MV_LEFT)
5340     oldx++;
5341   else if (direction == MV_RIGHT)
5342     oldx--;
5343   else if (direction == MV_UP)
5344     oldy++;
5345   else if (direction == MV_DOWN)
5346     oldy--;
5347
5348   *comes_from_x = oldx;
5349   *comes_from_y = oldy;
5350 }
5351
5352 int MovingOrBlocked2Element(int x, int y)
5353 {
5354   int element = Feld[x][y];
5355
5356   if (element == EL_BLOCKED)
5357   {
5358     int oldx, oldy;
5359
5360     Blocked2Moving(x, y, &oldx, &oldy);
5361     return Feld[oldx][oldy];
5362   }
5363   else
5364     return element;
5365 }
5366
5367 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5368 {
5369   /* like MovingOrBlocked2Element(), but if element is moving
5370      and (x,y) is the field the moving element is just leaving,
5371      return EL_BLOCKED instead of the element value */
5372   int element = Feld[x][y];
5373
5374   if (IS_MOVING(x, y))
5375   {
5376     if (element == EL_BLOCKED)
5377     {
5378       int oldx, oldy;
5379
5380       Blocked2Moving(x, y, &oldx, &oldy);
5381       return Feld[oldx][oldy];
5382     }
5383     else
5384       return EL_BLOCKED;
5385   }
5386   else
5387     return element;
5388 }
5389
5390 static void RemoveField(int x, int y)
5391 {
5392   Feld[x][y] = EL_EMPTY;
5393
5394   MovPos[x][y] = 0;
5395   MovDir[x][y] = 0;
5396   MovDelay[x][y] = 0;
5397
5398 #if USE_NEW_CUSTOM_VALUE
5399   CustomValue[x][y] = 0;
5400 #endif
5401
5402   AmoebaNr[x][y] = 0;
5403   ChangeDelay[x][y] = 0;
5404   ChangePage[x][y] = -1;
5405   Pushed[x][y] = FALSE;
5406
5407 #if 0
5408   ExplodeField[x][y] = EX_TYPE_NONE;
5409 #endif
5410
5411   GfxElement[x][y] = EL_UNDEFINED;
5412   GfxAction[x][y] = ACTION_DEFAULT;
5413   GfxDir[x][y] = MV_NONE;
5414 #if 0
5415   /* !!! this would prevent the removed tile from being redrawn !!! */
5416   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5417 #endif
5418 }
5419
5420 void RemoveMovingField(int x, int y)
5421 {
5422   int oldx = x, oldy = y, newx = x, newy = y;
5423   int element = Feld[x][y];
5424   int next_element = EL_UNDEFINED;
5425
5426   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5427     return;
5428
5429   if (IS_MOVING(x, y))
5430   {
5431     Moving2Blocked(x, y, &newx, &newy);
5432
5433     if (Feld[newx][newy] != EL_BLOCKED)
5434     {
5435       /* element is moving, but target field is not free (blocked), but
5436          already occupied by something different (example: acid pool);
5437          in this case, only remove the moving field, but not the target */
5438
5439       RemoveField(oldx, oldy);
5440
5441       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5442
5443       TEST_DrawLevelField(oldx, oldy);
5444
5445       return;
5446     }
5447   }
5448   else if (element == EL_BLOCKED)
5449   {
5450     Blocked2Moving(x, y, &oldx, &oldy);
5451     if (!IS_MOVING(oldx, oldy))
5452       return;
5453   }
5454
5455   if (element == EL_BLOCKED &&
5456       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5457        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5458        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5459        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5460        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5461        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5462     next_element = get_next_element(Feld[oldx][oldy]);
5463
5464   RemoveField(oldx, oldy);
5465   RemoveField(newx, newy);
5466
5467   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5468
5469   if (next_element != EL_UNDEFINED)
5470     Feld[oldx][oldy] = next_element;
5471
5472   TEST_DrawLevelField(oldx, oldy);
5473   TEST_DrawLevelField(newx, newy);
5474 }
5475
5476 void DrawDynamite(int x, int y)
5477 {
5478   int sx = SCREENX(x), sy = SCREENY(y);
5479   int graphic = el2img(Feld[x][y]);
5480   int frame;
5481
5482   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5483     return;
5484
5485   if (IS_WALKABLE_INSIDE(Back[x][y]))
5486     return;
5487
5488   if (Back[x][y])
5489     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5490   else if (Store[x][y])
5491     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5492
5493   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5494
5495   if (Back[x][y] || Store[x][y])
5496     DrawGraphicThruMask(sx, sy, graphic, frame);
5497   else
5498     DrawGraphic(sx, sy, graphic, frame);
5499 }
5500
5501 void CheckDynamite(int x, int y)
5502 {
5503   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5504   {
5505     MovDelay[x][y]--;
5506
5507     if (MovDelay[x][y] != 0)
5508     {
5509       DrawDynamite(x, y);
5510       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5511
5512       return;
5513     }
5514   }
5515
5516   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5517
5518   Bang(x, y);
5519 }
5520
5521 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5522 {
5523   boolean num_checked_players = 0;
5524   int i;
5525
5526   for (i = 0; i < MAX_PLAYERS; i++)
5527   {
5528     if (stored_player[i].active)
5529     {
5530       int sx = stored_player[i].jx;
5531       int sy = stored_player[i].jy;
5532
5533       if (num_checked_players == 0)
5534       {
5535         *sx1 = *sx2 = sx;
5536         *sy1 = *sy2 = sy;
5537       }
5538       else
5539       {
5540         *sx1 = MIN(*sx1, sx);
5541         *sy1 = MIN(*sy1, sy);
5542         *sx2 = MAX(*sx2, sx);
5543         *sy2 = MAX(*sy2, sy);
5544       }
5545
5546       num_checked_players++;
5547     }
5548   }
5549 }
5550
5551 static boolean checkIfAllPlayersFitToScreen_RND()
5552 {
5553   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5554
5555   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5556
5557   return (sx2 - sx1 < SCR_FIELDX &&
5558           sy2 - sy1 < SCR_FIELDY);
5559 }
5560
5561 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5562 {
5563   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5564
5565   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5566
5567   *sx = (sx1 + sx2) / 2;
5568   *sy = (sy1 + sy2) / 2;
5569 }
5570
5571 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5572                         boolean center_screen, boolean quick_relocation)
5573 {
5574   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5575   boolean no_delay = (tape.warp_forward);
5576   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5577   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5578
5579   if (quick_relocation)
5580   {
5581     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5582     {
5583       if (!level.shifted_relocation || center_screen)
5584       {
5585         /* quick relocation (without scrolling), with centering of screen */
5586
5587         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5588                     x > SBX_Right + MIDPOSX ? SBX_Right :
5589                     x - MIDPOSX);
5590
5591         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5592                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5593                     y - MIDPOSY);
5594       }
5595       else
5596       {
5597         /* quick relocation (without scrolling), but do not center screen */
5598
5599         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5600                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5601                                old_x - MIDPOSX);
5602
5603         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5604                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5605                                old_y - MIDPOSY);
5606
5607         int offset_x = x + (scroll_x - center_scroll_x);
5608         int offset_y = y + (scroll_y - center_scroll_y);
5609
5610         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5611                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5612                     offset_x - MIDPOSX);
5613
5614         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5615                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5616                     offset_y - MIDPOSY);
5617       }
5618     }
5619     else
5620     {
5621 #if 1
5622       if (!level.shifted_relocation || center_screen)
5623       {
5624         /* quick relocation (without scrolling), with centering of screen */
5625
5626         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5627                     x > SBX_Right + MIDPOSX ? SBX_Right :
5628                     x - MIDPOSX);
5629
5630         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5631                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5632                     y - MIDPOSY);
5633       }
5634       else
5635       {
5636         /* quick relocation (without scrolling), but do not center screen */
5637
5638         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5639                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5640                                old_x - MIDPOSX);
5641
5642         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5643                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5644                                old_y - MIDPOSY);
5645
5646         int offset_x = x + (scroll_x - center_scroll_x);
5647         int offset_y = y + (scroll_y - center_scroll_y);
5648
5649         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5650                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5651                     offset_x - MIDPOSX);
5652
5653         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655                     offset_y - MIDPOSY);
5656       }
5657 #else
5658       /* quick relocation (without scrolling), inside visible screen area */
5659
5660       int offset = game.scroll_delay_value;
5661
5662       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5663           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5664         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5665
5666       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5667           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5668         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5669
5670       /* don't scroll over playfield boundaries */
5671       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5672         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5673
5674       /* don't scroll over playfield boundaries */
5675       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5676         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5677 #endif
5678     }
5679
5680     RedrawPlayfield(TRUE, 0,0,0,0);
5681   }
5682   else
5683   {
5684 #if 1
5685     int scroll_xx, scroll_yy;
5686
5687     if (!level.shifted_relocation || center_screen)
5688     {
5689       /* visible relocation (with scrolling), with centering of screen */
5690
5691       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5692                    x > SBX_Right + MIDPOSX ? SBX_Right :
5693                    x - MIDPOSX);
5694
5695       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5696                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5697                    y - MIDPOSY);
5698     }
5699     else
5700     {
5701       /* visible relocation (with scrolling), but do not center screen */
5702
5703       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5704                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5705                              old_x - MIDPOSX);
5706
5707       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5708                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5709                              old_y - MIDPOSY);
5710
5711       int offset_x = x + (scroll_x - center_scroll_x);
5712       int offset_y = y + (scroll_y - center_scroll_y);
5713
5714       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5715                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5716                    offset_x - MIDPOSX);
5717
5718       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5719                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5720                    offset_y - MIDPOSY);
5721     }
5722
5723 #else
5724
5725     /* visible relocation (with scrolling), with centering of screen */
5726
5727     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5728                      x > SBX_Right + MIDPOSX ? SBX_Right :
5729                      x - MIDPOSX);
5730
5731     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5732                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5733                      y - MIDPOSY);
5734 #endif
5735
5736     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5737
5738     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5739     {
5740       int dx = 0, dy = 0;
5741       int fx = FX, fy = FY;
5742
5743       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5744       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5745
5746       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5747         break;
5748
5749       scroll_x -= dx;
5750       scroll_y -= dy;
5751
5752       fx += dx * TILEX / 2;
5753       fy += dy * TILEY / 2;
5754
5755       ScrollLevel(dx, dy);
5756       DrawAllPlayers();
5757
5758       /* scroll in two steps of half tile size to make things smoother */
5759       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5760       FlushDisplay();
5761       Delay(wait_delay_value);
5762
5763       /* scroll second step to align at full tile size */
5764       BackToFront();
5765       Delay(wait_delay_value);
5766     }
5767
5768     DrawAllPlayers();
5769     BackToFront();
5770     Delay(wait_delay_value);
5771   }
5772 }
5773
5774 void RelocatePlayer(int jx, int jy, int el_player_raw)
5775 {
5776   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5777   int player_nr = GET_PLAYER_NR(el_player);
5778   struct PlayerInfo *player = &stored_player[player_nr];
5779   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5780   boolean no_delay = (tape.warp_forward);
5781   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5782   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5783   int old_jx = player->jx;
5784   int old_jy = player->jy;
5785   int old_element = Feld[old_jx][old_jy];
5786   int element = Feld[jx][jy];
5787   boolean player_relocated = (old_jx != jx || old_jy != jy);
5788
5789   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5790   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5791   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5792   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5793   int leave_side_horiz = move_dir_horiz;
5794   int leave_side_vert  = move_dir_vert;
5795   int enter_side = enter_side_horiz | enter_side_vert;
5796   int leave_side = leave_side_horiz | leave_side_vert;
5797
5798   if (player->GameOver)         /* do not reanimate dead player */
5799     return;
5800
5801   if (!player_relocated)        /* no need to relocate the player */
5802     return;
5803
5804   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5805   {
5806     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5807     DrawLevelField(jx, jy);
5808   }
5809
5810   if (player->present)
5811   {
5812     while (player->MovPos)
5813     {
5814       ScrollPlayer(player, SCROLL_GO_ON);
5815       ScrollScreen(NULL, SCROLL_GO_ON);
5816
5817       AdvanceFrameAndPlayerCounters(player->index_nr);
5818
5819       DrawPlayer(player);
5820
5821       BackToFront();
5822       Delay(wait_delay_value);
5823     }
5824
5825     DrawPlayer(player);         /* needed here only to cleanup last field */
5826     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5827
5828     player->is_moving = FALSE;
5829   }
5830
5831   if (IS_CUSTOM_ELEMENT(old_element))
5832     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5833                                CE_LEFT_BY_PLAYER,
5834                                player->index_bit, leave_side);
5835
5836   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5837                                       CE_PLAYER_LEAVES_X,
5838                                       player->index_bit, leave_side);
5839
5840   Feld[jx][jy] = el_player;
5841   InitPlayerField(jx, jy, el_player, TRUE);
5842
5843   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5844      possible that the relocation target field did not contain a player element,
5845      but a walkable element, to which the new player was relocated -- in this
5846      case, restore that (already initialized!) element on the player field */
5847   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5848   {
5849     Feld[jx][jy] = element;     /* restore previously existing element */
5850 #if 0
5851     /* !!! do not initialize already initialized element a second time !!! */
5852     /* (this causes at least problems with "element creation" CE trigger for
5853        already existing elements, and existing Sokoban fields counted twice) */
5854     InitField(jx, jy, FALSE);
5855 #endif
5856   }
5857
5858   /* only visually relocate centered player */
5859   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5860                      FALSE, level.instant_relocation);
5861
5862   TestIfPlayerTouchesBadThing(jx, jy);
5863   TestIfPlayerTouchesCustomElement(jx, jy);
5864
5865   if (IS_CUSTOM_ELEMENT(element))
5866     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5867                                player->index_bit, enter_side);
5868
5869   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5870                                       player->index_bit, enter_side);
5871
5872 #if 1
5873   if (player->is_switching)
5874   {
5875     /* ensure that relocation while still switching an element does not cause
5876        a new element to be treated as also switched directly after relocation
5877        (this is important for teleporter switches that teleport the player to
5878        a place where another teleporter switch is in the same direction, which
5879        would then incorrectly be treated as immediately switched before the
5880        direction key that caused the switch was released) */
5881
5882     player->switch_x += jx - old_jx;
5883     player->switch_y += jy - old_jy;
5884   }
5885 #endif
5886 }
5887
5888 void Explode(int ex, int ey, int phase, int mode)
5889 {
5890   int x, y;
5891   int last_phase;
5892   int border_element;
5893
5894   /* !!! eliminate this variable !!! */
5895   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5896
5897   if (game.explosions_delayed)
5898   {
5899     ExplodeField[ex][ey] = mode;
5900     return;
5901   }
5902
5903   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5904   {
5905     int center_element = Feld[ex][ey];
5906     int artwork_element, explosion_element;     /* set these values later */
5907
5908 #if 0
5909     /* --- This is only really needed (and now handled) in "Impact()". --- */
5910     /* do not explode moving elements that left the explode field in time */
5911     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5912         center_element == EL_EMPTY &&
5913         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5914       return;
5915 #endif
5916
5917 #if 0
5918     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5919     if (mode == EX_TYPE_NORMAL ||
5920         mode == EX_TYPE_CENTER ||
5921         mode == EX_TYPE_CROSS)
5922       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5923 #endif
5924
5925     /* remove things displayed in background while burning dynamite */
5926     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5927       Back[ex][ey] = 0;
5928
5929     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5930     {
5931       /* put moving element to center field (and let it explode there) */
5932       center_element = MovingOrBlocked2Element(ex, ey);
5933       RemoveMovingField(ex, ey);
5934       Feld[ex][ey] = center_element;
5935     }
5936
5937     /* now "center_element" is finally determined -- set related values now */
5938     artwork_element = center_element;           /* for custom player artwork */
5939     explosion_element = center_element;         /* for custom player artwork */
5940
5941     if (IS_PLAYER(ex, ey))
5942     {
5943       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5944
5945       artwork_element = stored_player[player_nr].artwork_element;
5946
5947       if (level.use_explosion_element[player_nr])
5948       {
5949         explosion_element = level.explosion_element[player_nr];
5950         artwork_element = explosion_element;
5951       }
5952     }
5953
5954 #if 1
5955     if (mode == EX_TYPE_NORMAL ||
5956         mode == EX_TYPE_CENTER ||
5957         mode == EX_TYPE_CROSS)
5958       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5959 #endif
5960
5961     last_phase = element_info[explosion_element].explosion_delay + 1;
5962
5963     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5964     {
5965       int xx = x - ex + 1;
5966       int yy = y - ey + 1;
5967       int element;
5968
5969       if (!IN_LEV_FIELD(x, y) ||
5970           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5971           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5972         continue;
5973
5974       element = Feld[x][y];
5975
5976       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5977       {
5978         element = MovingOrBlocked2Element(x, y);
5979
5980         if (!IS_EXPLOSION_PROOF(element))
5981           RemoveMovingField(x, y);
5982       }
5983
5984       /* indestructible elements can only explode in center (but not flames) */
5985       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5986                                            mode == EX_TYPE_BORDER)) ||
5987           element == EL_FLAMES)
5988         continue;
5989
5990       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5991          behaviour, for example when touching a yamyam that explodes to rocks
5992          with active deadly shield, a rock is created under the player !!! */
5993       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5994 #if 0
5995       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5996           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5997            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5998 #else
5999       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6000 #endif
6001       {
6002         if (IS_ACTIVE_BOMB(element))
6003         {
6004           /* re-activate things under the bomb like gate or penguin */
6005           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6006           Back[x][y] = 0;
6007         }
6008
6009         continue;
6010       }
6011
6012       /* save walkable background elements while explosion on same tile */
6013       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6014           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6015         Back[x][y] = element;
6016
6017       /* ignite explodable elements reached by other explosion */
6018       if (element == EL_EXPLOSION)
6019         element = Store2[x][y];
6020
6021       if (AmoebaNr[x][y] &&
6022           (element == EL_AMOEBA_FULL ||
6023            element == EL_BD_AMOEBA ||
6024            element == EL_AMOEBA_GROWING))
6025       {
6026         AmoebaCnt[AmoebaNr[x][y]]--;
6027         AmoebaCnt2[AmoebaNr[x][y]]--;
6028       }
6029
6030       RemoveField(x, y);
6031
6032       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6033       {
6034         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6035
6036         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6037
6038         if (PLAYERINFO(ex, ey)->use_murphy)
6039           Store[x][y] = EL_EMPTY;
6040       }
6041
6042       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6043          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6044       else if (ELEM_IS_PLAYER(center_element))
6045         Store[x][y] = EL_EMPTY;
6046       else if (center_element == EL_YAMYAM)
6047         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6048       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6049         Store[x][y] = element_info[center_element].content.e[xx][yy];
6050 #if 1
6051       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6052          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6053          otherwise) -- FIX THIS !!! */
6054       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6055         Store[x][y] = element_info[element].content.e[1][1];
6056 #else
6057       else if (!CAN_EXPLODE(element))
6058         Store[x][y] = element_info[element].content.e[1][1];
6059 #endif
6060       else
6061         Store[x][y] = EL_EMPTY;
6062
6063       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6064           center_element == EL_AMOEBA_TO_DIAMOND)
6065         Store2[x][y] = element;
6066
6067       Feld[x][y] = EL_EXPLOSION;
6068       GfxElement[x][y] = artwork_element;
6069
6070       ExplodePhase[x][y] = 1;
6071       ExplodeDelay[x][y] = last_phase;
6072
6073       Stop[x][y] = TRUE;
6074     }
6075
6076     if (center_element == EL_YAMYAM)
6077       game.yamyam_content_nr =
6078         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6079
6080     return;
6081   }
6082
6083   if (Stop[ex][ey])
6084     return;
6085
6086   x = ex;
6087   y = ey;
6088
6089   if (phase == 1)
6090     GfxFrame[x][y] = 0;         /* restart explosion animation */
6091
6092   last_phase = ExplodeDelay[x][y];
6093
6094   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6095
6096 #ifdef DEBUG
6097
6098   /* activate this even in non-DEBUG version until cause for crash in
6099      getGraphicAnimationFrame() (see below) is found and eliminated */
6100
6101 #endif
6102 #if 1
6103
6104 #if 1
6105   /* this can happen if the player leaves an explosion just in time */
6106   if (GfxElement[x][y] == EL_UNDEFINED)
6107     GfxElement[x][y] = EL_EMPTY;
6108 #else
6109   if (GfxElement[x][y] == EL_UNDEFINED)
6110   {
6111     printf("\n\n");
6112     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6113     printf("Explode(): This should never happen!\n");
6114     printf("\n\n");
6115
6116     GfxElement[x][y] = EL_EMPTY;
6117   }
6118 #endif
6119
6120 #endif
6121
6122   border_element = Store2[x][y];
6123   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6124     border_element = StorePlayer[x][y];
6125
6126   if (phase == element_info[border_element].ignition_delay ||
6127       phase == last_phase)
6128   {
6129     boolean border_explosion = FALSE;
6130
6131     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6132         !PLAYER_EXPLOSION_PROTECTED(x, y))
6133     {
6134       KillPlayerUnlessExplosionProtected(x, y);
6135       border_explosion = TRUE;
6136     }
6137     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6138     {
6139       Feld[x][y] = Store2[x][y];
6140       Store2[x][y] = 0;
6141       Bang(x, y);
6142       border_explosion = TRUE;
6143     }
6144     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6145     {
6146       AmoebeUmwandeln(x, y);
6147       Store2[x][y] = 0;
6148       border_explosion = TRUE;
6149     }
6150
6151     /* if an element just explodes due to another explosion (chain-reaction),
6152        do not immediately end the new explosion when it was the last frame of
6153        the explosion (as it would be done in the following "if"-statement!) */
6154     if (border_explosion && phase == last_phase)
6155       return;
6156   }
6157
6158   if (phase == last_phase)
6159   {
6160     int element;
6161
6162     element = Feld[x][y] = Store[x][y];
6163     Store[x][y] = Store2[x][y] = 0;
6164     GfxElement[x][y] = EL_UNDEFINED;
6165
6166     /* player can escape from explosions and might therefore be still alive */
6167     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6168         element <= EL_PLAYER_IS_EXPLODING_4)
6169     {
6170       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6171       int explosion_element = EL_PLAYER_1 + player_nr;
6172       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6173       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6174
6175       if (level.use_explosion_element[player_nr])
6176         explosion_element = level.explosion_element[player_nr];
6177
6178       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6179                     element_info[explosion_element].content.e[xx][yy]);
6180     }
6181
6182     /* restore probably existing indestructible background element */
6183     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6184       element = Feld[x][y] = Back[x][y];
6185     Back[x][y] = 0;
6186
6187     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6188     GfxDir[x][y] = MV_NONE;
6189     ChangeDelay[x][y] = 0;
6190     ChangePage[x][y] = -1;
6191
6192 #if USE_NEW_CUSTOM_VALUE
6193     CustomValue[x][y] = 0;
6194 #endif
6195
6196     InitField_WithBug2(x, y, FALSE);
6197
6198     TEST_DrawLevelField(x, y);
6199
6200     TestIfElementTouchesCustomElement(x, y);
6201
6202     if (GFX_CRUMBLED(element))
6203       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6204
6205     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6206       StorePlayer[x][y] = 0;
6207
6208     if (ELEM_IS_PLAYER(element))
6209       RelocatePlayer(x, y, element);
6210   }
6211   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6212   {
6213     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6214     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6215
6216     if (phase == delay)
6217       TEST_DrawLevelFieldCrumbled(x, y);
6218
6219     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6220     {
6221       DrawLevelElement(x, y, Back[x][y]);
6222       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6223     }
6224     else if (IS_WALKABLE_UNDER(Back[x][y]))
6225     {
6226       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6227       DrawLevelElementThruMask(x, y, Back[x][y]);
6228     }
6229     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6230       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6231   }
6232 }
6233
6234 void DynaExplode(int ex, int ey)
6235 {
6236   int i, j;
6237   int dynabomb_element = Feld[ex][ey];
6238   int dynabomb_size = 1;
6239   boolean dynabomb_xl = FALSE;
6240   struct PlayerInfo *player;
6241   static int xy[4][2] =
6242   {
6243     { 0, -1 },
6244     { -1, 0 },
6245     { +1, 0 },
6246     { 0, +1 }
6247   };
6248
6249   if (IS_ACTIVE_BOMB(dynabomb_element))
6250   {
6251     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6252     dynabomb_size = player->dynabomb_size;
6253     dynabomb_xl = player->dynabomb_xl;
6254     player->dynabombs_left++;
6255   }
6256
6257   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6258
6259   for (i = 0; i < NUM_DIRECTIONS; i++)
6260   {
6261     for (j = 1; j <= dynabomb_size; j++)
6262     {
6263       int x = ex + j * xy[i][0];
6264       int y = ey + j * xy[i][1];
6265       int element;
6266
6267       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6268         break;
6269
6270       element = Feld[x][y];
6271
6272       /* do not restart explosions of fields with active bombs */
6273       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6274         continue;
6275
6276       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6277
6278       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6279           !IS_DIGGABLE(element) && !dynabomb_xl)
6280         break;
6281     }
6282   }
6283 }
6284
6285 void Bang(int x, int y)
6286 {
6287   int element = MovingOrBlocked2Element(x, y);
6288   int explosion_type = EX_TYPE_NORMAL;
6289
6290   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6291   {
6292     struct PlayerInfo *player = PLAYERINFO(x, y);
6293
6294 #if USE_FIX_CE_ACTION_WITH_PLAYER
6295     element = Feld[x][y] = player->initial_element;
6296 #else
6297     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6298                             player->element_nr);
6299 #endif
6300
6301     if (level.use_explosion_element[player->index_nr])
6302     {
6303       int explosion_element = level.explosion_element[player->index_nr];
6304
6305       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6306         explosion_type = EX_TYPE_CROSS;
6307       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6308         explosion_type = EX_TYPE_CENTER;
6309     }
6310   }
6311
6312   switch (element)
6313   {
6314     case EL_BUG:
6315     case EL_SPACESHIP:
6316     case EL_BD_BUTTERFLY:
6317     case EL_BD_FIREFLY:
6318     case EL_YAMYAM:
6319     case EL_DARK_YAMYAM:
6320     case EL_ROBOT:
6321     case EL_PACMAN:
6322     case EL_MOLE:
6323       RaiseScoreElement(element);
6324       break;
6325
6326     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6327     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6328     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6329     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6330     case EL_DYNABOMB_INCREASE_NUMBER:
6331     case EL_DYNABOMB_INCREASE_SIZE:
6332     case EL_DYNABOMB_INCREASE_POWER:
6333       explosion_type = EX_TYPE_DYNA;
6334       break;
6335
6336     case EL_DC_LANDMINE:
6337 #if 0
6338     case EL_EM_EXIT_OPEN:
6339     case EL_EM_STEEL_EXIT_OPEN:
6340 #endif
6341       explosion_type = EX_TYPE_CENTER;
6342       break;
6343
6344     case EL_PENGUIN:
6345     case EL_LAMP:
6346     case EL_LAMP_ACTIVE:
6347     case EL_AMOEBA_TO_DIAMOND:
6348       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6349         explosion_type = EX_TYPE_CENTER;
6350       break;
6351
6352     default:
6353       if (element_info[element].explosion_type == EXPLODES_CROSS)
6354         explosion_type = EX_TYPE_CROSS;
6355       else if (element_info[element].explosion_type == EXPLODES_1X1)
6356         explosion_type = EX_TYPE_CENTER;
6357       break;
6358   }
6359
6360   if (explosion_type == EX_TYPE_DYNA)
6361     DynaExplode(x, y);
6362   else
6363     Explode(x, y, EX_PHASE_START, explosion_type);
6364
6365   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6366 }
6367
6368 void SplashAcid(int x, int y)
6369 {
6370   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6371       (!IN_LEV_FIELD(x - 1, y - 2) ||
6372        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6373     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6374
6375   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6376       (!IN_LEV_FIELD(x + 1, y - 2) ||
6377        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6378     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6379
6380   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6381 }
6382
6383 static void InitBeltMovement()
6384 {
6385   static int belt_base_element[4] =
6386   {
6387     EL_CONVEYOR_BELT_1_LEFT,
6388     EL_CONVEYOR_BELT_2_LEFT,
6389     EL_CONVEYOR_BELT_3_LEFT,
6390     EL_CONVEYOR_BELT_4_LEFT
6391   };
6392   static int belt_base_active_element[4] =
6393   {
6394     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6395     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6396     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6397     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6398   };
6399
6400   int x, y, i, j;
6401
6402   /* set frame order for belt animation graphic according to belt direction */
6403   for (i = 0; i < NUM_BELTS; i++)
6404   {
6405     int belt_nr = i;
6406
6407     for (j = 0; j < NUM_BELT_PARTS; j++)
6408     {
6409       int element = belt_base_active_element[belt_nr] + j;
6410       int graphic_1 = el2img(element);
6411       int graphic_2 = el2panelimg(element);
6412
6413       if (game.belt_dir[i] == MV_LEFT)
6414       {
6415         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6416         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6417       }
6418       else
6419       {
6420         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6421         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6422       }
6423     }
6424   }
6425
6426   SCAN_PLAYFIELD(x, y)
6427   {
6428     int element = Feld[x][y];
6429
6430     for (i = 0; i < NUM_BELTS; i++)
6431     {
6432       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6433       {
6434         int e_belt_nr = getBeltNrFromBeltElement(element);
6435         int belt_nr = i;
6436
6437         if (e_belt_nr == belt_nr)
6438         {
6439           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6440
6441           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6442         }
6443       }
6444     }
6445   }
6446 }
6447
6448 static void ToggleBeltSwitch(int x, int y)
6449 {
6450   static int belt_base_element[4] =
6451   {
6452     EL_CONVEYOR_BELT_1_LEFT,
6453     EL_CONVEYOR_BELT_2_LEFT,
6454     EL_CONVEYOR_BELT_3_LEFT,
6455     EL_CONVEYOR_BELT_4_LEFT
6456   };
6457   static int belt_base_active_element[4] =
6458   {
6459     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6460     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6461     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6462     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6463   };
6464   static int belt_base_switch_element[4] =
6465   {
6466     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6467     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6468     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6469     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6470   };
6471   static int belt_move_dir[4] =
6472   {
6473     MV_LEFT,
6474     MV_NONE,
6475     MV_RIGHT,
6476     MV_NONE,
6477   };
6478
6479   int element = Feld[x][y];
6480   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6481   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6482   int belt_dir = belt_move_dir[belt_dir_nr];
6483   int xx, yy, i;
6484
6485   if (!IS_BELT_SWITCH(element))
6486     return;
6487
6488   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6489   game.belt_dir[belt_nr] = belt_dir;
6490
6491   if (belt_dir_nr == 3)
6492     belt_dir_nr = 1;
6493
6494   /* set frame order for belt animation graphic according to belt direction */
6495   for (i = 0; i < NUM_BELT_PARTS; i++)
6496   {
6497     int element = belt_base_active_element[belt_nr] + i;
6498     int graphic_1 = el2img(element);
6499     int graphic_2 = el2panelimg(element);
6500
6501     if (belt_dir == MV_LEFT)
6502     {
6503       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6504       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6505     }
6506     else
6507     {
6508       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6509       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6510     }
6511   }
6512
6513   SCAN_PLAYFIELD(xx, yy)
6514   {
6515     int element = Feld[xx][yy];
6516
6517     if (IS_BELT_SWITCH(element))
6518     {
6519       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6520
6521       if (e_belt_nr == belt_nr)
6522       {
6523         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6524         TEST_DrawLevelField(xx, yy);
6525       }
6526     }
6527     else if (IS_BELT(element) && belt_dir != MV_NONE)
6528     {
6529       int e_belt_nr = getBeltNrFromBeltElement(element);
6530
6531       if (e_belt_nr == belt_nr)
6532       {
6533         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6534
6535         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6536         TEST_DrawLevelField(xx, yy);
6537       }
6538     }
6539     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6540     {
6541       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6542
6543       if (e_belt_nr == belt_nr)
6544       {
6545         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6546
6547         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6548         TEST_DrawLevelField(xx, yy);
6549       }
6550     }
6551   }
6552 }
6553
6554 static void ToggleSwitchgateSwitch(int x, int y)
6555 {
6556   int xx, yy;
6557
6558   game.switchgate_pos = !game.switchgate_pos;
6559
6560   SCAN_PLAYFIELD(xx, yy)
6561   {
6562     int element = Feld[xx][yy];
6563
6564 #if !USE_BOTH_SWITCHGATE_SWITCHES
6565     if (element == EL_SWITCHGATE_SWITCH_UP ||
6566         element == EL_SWITCHGATE_SWITCH_DOWN)
6567     {
6568       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6569       TEST_DrawLevelField(xx, yy);
6570     }
6571     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6572              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6573     {
6574       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6575       TEST_DrawLevelField(xx, yy);
6576     }
6577 #else
6578     if (element == EL_SWITCHGATE_SWITCH_UP)
6579     {
6580       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6581       TEST_DrawLevelField(xx, yy);
6582     }
6583     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6584     {
6585       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6586       TEST_DrawLevelField(xx, yy);
6587     }
6588     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6589     {
6590       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6591       TEST_DrawLevelField(xx, yy);
6592     }
6593     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6594     {
6595       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6596       TEST_DrawLevelField(xx, yy);
6597     }
6598 #endif
6599     else if (element == EL_SWITCHGATE_OPEN ||
6600              element == EL_SWITCHGATE_OPENING)
6601     {
6602       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6603
6604       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6605     }
6606     else if (element == EL_SWITCHGATE_CLOSED ||
6607              element == EL_SWITCHGATE_CLOSING)
6608     {
6609       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6610
6611       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6612     }
6613   }
6614 }
6615
6616 static int getInvisibleActiveFromInvisibleElement(int element)
6617 {
6618   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6619           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6620           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6621           element);
6622 }
6623
6624 static int getInvisibleFromInvisibleActiveElement(int element)
6625 {
6626   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6627           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6628           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6629           element);
6630 }
6631
6632 static void RedrawAllLightSwitchesAndInvisibleElements()
6633 {
6634   int x, y;
6635
6636   SCAN_PLAYFIELD(x, y)
6637   {
6638     int element = Feld[x][y];
6639
6640     if (element == EL_LIGHT_SWITCH &&
6641         game.light_time_left > 0)
6642     {
6643       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6644       TEST_DrawLevelField(x, y);
6645     }
6646     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6647              game.light_time_left == 0)
6648     {
6649       Feld[x][y] = EL_LIGHT_SWITCH;
6650       TEST_DrawLevelField(x, y);
6651     }
6652     else if (element == EL_EMC_DRIPPER &&
6653              game.light_time_left > 0)
6654     {
6655       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6656       TEST_DrawLevelField(x, y);
6657     }
6658     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6659              game.light_time_left == 0)
6660     {
6661       Feld[x][y] = EL_EMC_DRIPPER;
6662       TEST_DrawLevelField(x, y);
6663     }
6664     else if (element == EL_INVISIBLE_STEELWALL ||
6665              element == EL_INVISIBLE_WALL ||
6666              element == EL_INVISIBLE_SAND)
6667     {
6668       if (game.light_time_left > 0)
6669         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6670
6671       TEST_DrawLevelField(x, y);
6672
6673       /* uncrumble neighbour fields, if needed */
6674       if (element == EL_INVISIBLE_SAND)
6675         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6676     }
6677     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6678              element == EL_INVISIBLE_WALL_ACTIVE ||
6679              element == EL_INVISIBLE_SAND_ACTIVE)
6680     {
6681       if (game.light_time_left == 0)
6682         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6683
6684       TEST_DrawLevelField(x, y);
6685
6686       /* re-crumble neighbour fields, if needed */
6687       if (element == EL_INVISIBLE_SAND)
6688         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6689     }
6690   }
6691 }
6692
6693 static void RedrawAllInvisibleElementsForLenses()
6694 {
6695   int x, y;
6696
6697   SCAN_PLAYFIELD(x, y)
6698   {
6699     int element = Feld[x][y];
6700
6701     if (element == EL_EMC_DRIPPER &&
6702         game.lenses_time_left > 0)
6703     {
6704       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6705       TEST_DrawLevelField(x, y);
6706     }
6707     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6708              game.lenses_time_left == 0)
6709     {
6710       Feld[x][y] = EL_EMC_DRIPPER;
6711       TEST_DrawLevelField(x, y);
6712     }
6713     else if (element == EL_INVISIBLE_STEELWALL ||
6714              element == EL_INVISIBLE_WALL ||
6715              element == EL_INVISIBLE_SAND)
6716     {
6717       if (game.lenses_time_left > 0)
6718         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6719
6720       TEST_DrawLevelField(x, y);
6721
6722       /* uncrumble neighbour fields, if needed */
6723       if (element == EL_INVISIBLE_SAND)
6724         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6725     }
6726     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6727              element == EL_INVISIBLE_WALL_ACTIVE ||
6728              element == EL_INVISIBLE_SAND_ACTIVE)
6729     {
6730       if (game.lenses_time_left == 0)
6731         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6732
6733       TEST_DrawLevelField(x, y);
6734
6735       /* re-crumble neighbour fields, if needed */
6736       if (element == EL_INVISIBLE_SAND)
6737         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6738     }
6739   }
6740 }
6741
6742 static void RedrawAllInvisibleElementsForMagnifier()
6743 {
6744   int x, y;
6745
6746   SCAN_PLAYFIELD(x, y)
6747   {
6748     int element = Feld[x][y];
6749
6750     if (element == EL_EMC_FAKE_GRASS &&
6751         game.magnify_time_left > 0)
6752     {
6753       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6754       TEST_DrawLevelField(x, y);
6755     }
6756     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6757              game.magnify_time_left == 0)
6758     {
6759       Feld[x][y] = EL_EMC_FAKE_GRASS;
6760       TEST_DrawLevelField(x, y);
6761     }
6762     else if (IS_GATE_GRAY(element) &&
6763              game.magnify_time_left > 0)
6764     {
6765       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6766                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6767                     IS_EM_GATE_GRAY(element) ?
6768                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6769                     IS_EMC_GATE_GRAY(element) ?
6770                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6771                     IS_DC_GATE_GRAY(element) ?
6772                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6773                     element);
6774       TEST_DrawLevelField(x, y);
6775     }
6776     else if (IS_GATE_GRAY_ACTIVE(element) &&
6777              game.magnify_time_left == 0)
6778     {
6779       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6780                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6781                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6782                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6783                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6784                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6785                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6786                     EL_DC_GATE_WHITE_GRAY :
6787                     element);
6788       TEST_DrawLevelField(x, y);
6789     }
6790   }
6791 }
6792
6793 static void ToggleLightSwitch(int x, int y)
6794 {
6795   int element = Feld[x][y];
6796
6797   game.light_time_left =
6798     (element == EL_LIGHT_SWITCH ?
6799      level.time_light * FRAMES_PER_SECOND : 0);
6800
6801   RedrawAllLightSwitchesAndInvisibleElements();
6802 }
6803
6804 static void ActivateTimegateSwitch(int x, int y)
6805 {
6806   int xx, yy;
6807
6808   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6809
6810   SCAN_PLAYFIELD(xx, yy)
6811   {
6812     int element = Feld[xx][yy];
6813
6814     if (element == EL_TIMEGATE_CLOSED ||
6815         element == EL_TIMEGATE_CLOSING)
6816     {
6817       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6818       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6819     }
6820
6821     /*
6822     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6823     {
6824       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6825       TEST_DrawLevelField(xx, yy);
6826     }
6827     */
6828
6829   }
6830
6831 #if 1
6832   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6833                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6834 #else
6835   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6836 #endif
6837 }
6838
6839 void Impact(int x, int y)
6840 {
6841   boolean last_line = (y == lev_fieldy - 1);
6842   boolean object_hit = FALSE;
6843   boolean impact = (last_line || object_hit);
6844   int element = Feld[x][y];
6845   int smashed = EL_STEELWALL;
6846
6847   if (!last_line)       /* check if element below was hit */
6848   {
6849     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6850       return;
6851
6852     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6853                                          MovDir[x][y + 1] != MV_DOWN ||
6854                                          MovPos[x][y + 1] <= TILEY / 2));
6855
6856     /* do not smash moving elements that left the smashed field in time */
6857     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6858         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6859       object_hit = FALSE;
6860
6861 #if USE_QUICKSAND_IMPACT_BUGFIX
6862     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6863     {
6864       RemoveMovingField(x, y + 1);
6865       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6866       Feld[x][y + 2] = EL_ROCK;
6867       TEST_DrawLevelField(x, y + 2);
6868
6869       object_hit = TRUE;
6870     }
6871
6872     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6873     {
6874       RemoveMovingField(x, y + 1);
6875       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6876       Feld[x][y + 2] = EL_ROCK;
6877       TEST_DrawLevelField(x, y + 2);
6878
6879       object_hit = TRUE;
6880     }
6881 #endif
6882
6883     if (object_hit)
6884       smashed = MovingOrBlocked2Element(x, y + 1);
6885
6886     impact = (last_line || object_hit);
6887   }
6888
6889   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6890   {
6891     SplashAcid(x, y + 1);
6892     return;
6893   }
6894
6895   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6896   /* only reset graphic animation if graphic really changes after impact */
6897   if (impact &&
6898       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6899   {
6900     ResetGfxAnimation(x, y);
6901     TEST_DrawLevelField(x, y);
6902   }
6903
6904   if (impact && CAN_EXPLODE_IMPACT(element))
6905   {
6906     Bang(x, y);
6907     return;
6908   }
6909   else if (impact && element == EL_PEARL &&
6910            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6911   {
6912     ResetGfxAnimation(x, y);
6913
6914     Feld[x][y] = EL_PEARL_BREAKING;
6915     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6916     return;
6917   }
6918   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6919   {
6920     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6921
6922     return;
6923   }
6924
6925   if (impact && element == EL_AMOEBA_DROP)
6926   {
6927     if (object_hit && IS_PLAYER(x, y + 1))
6928       KillPlayerUnlessEnemyProtected(x, y + 1);
6929     else if (object_hit && smashed == EL_PENGUIN)
6930       Bang(x, y + 1);
6931     else
6932     {
6933       Feld[x][y] = EL_AMOEBA_GROWING;
6934       Store[x][y] = EL_AMOEBA_WET;
6935
6936       ResetRandomAnimationValue(x, y);
6937     }
6938     return;
6939   }
6940
6941   if (object_hit)               /* check which object was hit */
6942   {
6943     if ((CAN_PASS_MAGIC_WALL(element) && 
6944          (smashed == EL_MAGIC_WALL ||
6945           smashed == EL_BD_MAGIC_WALL)) ||
6946         (CAN_PASS_DC_MAGIC_WALL(element) &&
6947          smashed == EL_DC_MAGIC_WALL))
6948     {
6949       int xx, yy;
6950       int activated_magic_wall =
6951         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6952          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6953          EL_DC_MAGIC_WALL_ACTIVE);
6954
6955       /* activate magic wall / mill */
6956       SCAN_PLAYFIELD(xx, yy)
6957       {
6958         if (Feld[xx][yy] == smashed)
6959           Feld[xx][yy] = activated_magic_wall;
6960       }
6961
6962       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6963       game.magic_wall_active = TRUE;
6964
6965       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6966                             SND_MAGIC_WALL_ACTIVATING :
6967                             smashed == EL_BD_MAGIC_WALL ?
6968                             SND_BD_MAGIC_WALL_ACTIVATING :
6969                             SND_DC_MAGIC_WALL_ACTIVATING));
6970     }
6971
6972     if (IS_PLAYER(x, y + 1))
6973     {
6974       if (CAN_SMASH_PLAYER(element))
6975       {
6976         KillPlayerUnlessEnemyProtected(x, y + 1);
6977         return;
6978       }
6979     }
6980     else if (smashed == EL_PENGUIN)
6981     {
6982       if (CAN_SMASH_PLAYER(element))
6983       {
6984         Bang(x, y + 1);
6985         return;
6986       }
6987     }
6988     else if (element == EL_BD_DIAMOND)
6989     {
6990       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6991       {
6992         Bang(x, y + 1);
6993         return;
6994       }
6995     }
6996     else if (((element == EL_SP_INFOTRON ||
6997                element == EL_SP_ZONK) &&
6998               (smashed == EL_SP_SNIKSNAK ||
6999                smashed == EL_SP_ELECTRON ||
7000                smashed == EL_SP_DISK_ORANGE)) ||
7001              (element == EL_SP_INFOTRON &&
7002               smashed == EL_SP_DISK_YELLOW))
7003     {
7004       Bang(x, y + 1);
7005       return;
7006     }
7007     else if (CAN_SMASH_EVERYTHING(element))
7008     {
7009       if (IS_CLASSIC_ENEMY(smashed) ||
7010           CAN_EXPLODE_SMASHED(smashed))
7011       {
7012         Bang(x, y + 1);
7013         return;
7014       }
7015       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7016       {
7017         if (smashed == EL_LAMP ||
7018             smashed == EL_LAMP_ACTIVE)
7019         {
7020           Bang(x, y + 1);
7021           return;
7022         }
7023         else if (smashed == EL_NUT)
7024         {
7025           Feld[x][y + 1] = EL_NUT_BREAKING;
7026           PlayLevelSound(x, y, SND_NUT_BREAKING);
7027           RaiseScoreElement(EL_NUT);
7028           return;
7029         }
7030         else if (smashed == EL_PEARL)
7031         {
7032           ResetGfxAnimation(x, y);
7033
7034           Feld[x][y + 1] = EL_PEARL_BREAKING;
7035           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7036           return;
7037         }
7038         else if (smashed == EL_DIAMOND)
7039         {
7040           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7041           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7042           return;
7043         }
7044         else if (IS_BELT_SWITCH(smashed))
7045         {
7046           ToggleBeltSwitch(x, y + 1);
7047         }
7048         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7049                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7050                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7051                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7052         {
7053           ToggleSwitchgateSwitch(x, y + 1);
7054         }
7055         else if (smashed == EL_LIGHT_SWITCH ||
7056                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7057         {
7058           ToggleLightSwitch(x, y + 1);
7059         }
7060         else
7061         {
7062 #if 0
7063           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7064 #endif
7065
7066           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7067
7068           CheckElementChangeBySide(x, y + 1, smashed, element,
7069                                    CE_SWITCHED, CH_SIDE_TOP);
7070           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7071                                             CH_SIDE_TOP);
7072         }
7073       }
7074       else
7075       {
7076         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7077       }
7078     }
7079   }
7080
7081   /* play sound of magic wall / mill */
7082   if (!last_line &&
7083       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7084        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7085        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7086   {
7087     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7088       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7089     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7090       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7091     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7092       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7093
7094     return;
7095   }
7096
7097   /* play sound of object that hits the ground */
7098   if (last_line || object_hit)
7099     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7100 }
7101
7102 inline static void TurnRoundExt(int x, int y)
7103 {
7104   static struct
7105   {
7106     int dx, dy;
7107   } move_xy[] =
7108   {
7109     {  0,  0 },
7110     { -1,  0 },
7111     { +1,  0 },
7112     {  0,  0 },
7113     {  0, -1 },
7114     {  0,  0 }, { 0, 0 }, { 0, 0 },
7115     {  0, +1 }
7116   };
7117   static struct
7118   {
7119     int left, right, back;
7120   } turn[] =
7121   {
7122     { 0,        0,              0        },
7123     { MV_DOWN,  MV_UP,          MV_RIGHT },
7124     { MV_UP,    MV_DOWN,        MV_LEFT  },
7125     { 0,        0,              0        },
7126     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7127     { 0,        0,              0        },
7128     { 0,        0,              0        },
7129     { 0,        0,              0        },
7130     { MV_RIGHT, MV_LEFT,        MV_UP    }
7131   };
7132
7133   int element = Feld[x][y];
7134   int move_pattern = element_info[element].move_pattern;
7135
7136   int old_move_dir = MovDir[x][y];
7137   int left_dir  = turn[old_move_dir].left;
7138   int right_dir = turn[old_move_dir].right;
7139   int back_dir  = turn[old_move_dir].back;
7140
7141   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7142   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7143   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7144   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7145
7146   int left_x  = x + left_dx,  left_y  = y + left_dy;
7147   int right_x = x + right_dx, right_y = y + right_dy;
7148   int move_x  = x + move_dx,  move_y  = y + move_dy;
7149
7150   int xx, yy;
7151
7152   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7153   {
7154     TestIfBadThingTouchesOtherBadThing(x, y);
7155
7156     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7157       MovDir[x][y] = right_dir;
7158     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7159       MovDir[x][y] = left_dir;
7160
7161     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7162       MovDelay[x][y] = 9;
7163     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7164       MovDelay[x][y] = 1;
7165   }
7166   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7167   {
7168     TestIfBadThingTouchesOtherBadThing(x, y);
7169
7170     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7171       MovDir[x][y] = left_dir;
7172     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7173       MovDir[x][y] = right_dir;
7174
7175     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7176       MovDelay[x][y] = 9;
7177     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7178       MovDelay[x][y] = 1;
7179   }
7180   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7181   {
7182     TestIfBadThingTouchesOtherBadThing(x, y);
7183
7184     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7185       MovDir[x][y] = left_dir;
7186     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7187       MovDir[x][y] = right_dir;
7188
7189     if (MovDir[x][y] != old_move_dir)
7190       MovDelay[x][y] = 9;
7191   }
7192   else if (element == EL_YAMYAM)
7193   {
7194     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7195     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7196
7197     if (can_turn_left && can_turn_right)
7198       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7199     else if (can_turn_left)
7200       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7201     else if (can_turn_right)
7202       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7203     else
7204       MovDir[x][y] = back_dir;
7205
7206     MovDelay[x][y] = 16 + 16 * RND(3);
7207   }
7208   else if (element == EL_DARK_YAMYAM)
7209   {
7210     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7211                                                          left_x, left_y);
7212     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7213                                                          right_x, right_y);
7214
7215     if (can_turn_left && can_turn_right)
7216       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7217     else if (can_turn_left)
7218       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7219     else if (can_turn_right)
7220       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7221     else
7222       MovDir[x][y] = back_dir;
7223
7224     MovDelay[x][y] = 16 + 16 * RND(3);
7225   }
7226   else if (element == EL_PACMAN)
7227   {
7228     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7229     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7230
7231     if (can_turn_left && can_turn_right)
7232       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7233     else if (can_turn_left)
7234       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7235     else if (can_turn_right)
7236       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7237     else
7238       MovDir[x][y] = back_dir;
7239
7240     MovDelay[x][y] = 6 + RND(40);
7241   }
7242   else if (element == EL_PIG)
7243   {
7244     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7245     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7246     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7247     boolean should_turn_left, should_turn_right, should_move_on;
7248     int rnd_value = 24;
7249     int rnd = RND(rnd_value);
7250
7251     should_turn_left = (can_turn_left &&
7252                         (!can_move_on ||
7253                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7254                                                    y + back_dy + left_dy)));
7255     should_turn_right = (can_turn_right &&
7256                          (!can_move_on ||
7257                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7258                                                     y + back_dy + right_dy)));
7259     should_move_on = (can_move_on &&
7260                       (!can_turn_left ||
7261                        !can_turn_right ||
7262                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7263                                                  y + move_dy + left_dy) ||
7264                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7265                                                  y + move_dy + right_dy)));
7266
7267     if (should_turn_left || should_turn_right || should_move_on)
7268     {
7269       if (should_turn_left && should_turn_right && should_move_on)
7270         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7271                         rnd < 2 * rnd_value / 3 ? right_dir :
7272                         old_move_dir);
7273       else if (should_turn_left && should_turn_right)
7274         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7275       else if (should_turn_left && should_move_on)
7276         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7277       else if (should_turn_right && should_move_on)
7278         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7279       else if (should_turn_left)
7280         MovDir[x][y] = left_dir;
7281       else if (should_turn_right)
7282         MovDir[x][y] = right_dir;
7283       else if (should_move_on)
7284         MovDir[x][y] = old_move_dir;
7285     }
7286     else if (can_move_on && rnd > rnd_value / 8)
7287       MovDir[x][y] = old_move_dir;
7288     else if (can_turn_left && can_turn_right)
7289       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7290     else if (can_turn_left && rnd > rnd_value / 8)
7291       MovDir[x][y] = left_dir;
7292     else if (can_turn_right && rnd > rnd_value/8)
7293       MovDir[x][y] = right_dir;
7294     else
7295       MovDir[x][y] = back_dir;
7296
7297     xx = x + move_xy[MovDir[x][y]].dx;
7298     yy = y + move_xy[MovDir[x][y]].dy;
7299
7300     if (!IN_LEV_FIELD(xx, yy) ||
7301         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7302       MovDir[x][y] = old_move_dir;
7303
7304     MovDelay[x][y] = 0;
7305   }
7306   else if (element == EL_DRAGON)
7307   {
7308     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7309     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7310     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7311     int rnd_value = 24;
7312     int rnd = RND(rnd_value);
7313
7314     if (can_move_on && rnd > rnd_value / 8)
7315       MovDir[x][y] = old_move_dir;
7316     else if (can_turn_left && can_turn_right)
7317       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7318     else if (can_turn_left && rnd > rnd_value / 8)
7319       MovDir[x][y] = left_dir;
7320     else if (can_turn_right && rnd > rnd_value / 8)
7321       MovDir[x][y] = right_dir;
7322     else
7323       MovDir[x][y] = back_dir;
7324
7325     xx = x + move_xy[MovDir[x][y]].dx;
7326     yy = y + move_xy[MovDir[x][y]].dy;
7327
7328     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7329       MovDir[x][y] = old_move_dir;
7330
7331     MovDelay[x][y] = 0;
7332   }
7333   else if (element == EL_MOLE)
7334   {
7335     boolean can_move_on =
7336       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7337                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7338                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7339     if (!can_move_on)
7340     {
7341       boolean can_turn_left =
7342         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7343                               IS_AMOEBOID(Feld[left_x][left_y])));
7344
7345       boolean can_turn_right =
7346         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7347                               IS_AMOEBOID(Feld[right_x][right_y])));
7348
7349       if (can_turn_left && can_turn_right)
7350         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7351       else if (can_turn_left)
7352         MovDir[x][y] = left_dir;
7353       else
7354         MovDir[x][y] = right_dir;
7355     }
7356
7357     if (MovDir[x][y] != old_move_dir)
7358       MovDelay[x][y] = 9;
7359   }
7360   else if (element == EL_BALLOON)
7361   {
7362     MovDir[x][y] = game.wind_direction;
7363     MovDelay[x][y] = 0;
7364   }
7365   else if (element == EL_SPRING)
7366   {
7367 #if USE_NEW_SPRING_BUMPER
7368     if (MovDir[x][y] & MV_HORIZONTAL)
7369     {
7370       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7371           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7372       {
7373         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7374         ResetGfxAnimation(move_x, move_y);
7375         TEST_DrawLevelField(move_x, move_y);
7376
7377         MovDir[x][y] = back_dir;
7378       }
7379       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7380                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7381         MovDir[x][y] = MV_NONE;
7382     }
7383 #else
7384     if (MovDir[x][y] & MV_HORIZONTAL &&
7385         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7386          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7387       MovDir[x][y] = MV_NONE;
7388 #endif
7389
7390     MovDelay[x][y] = 0;
7391   }
7392   else if (element == EL_ROBOT ||
7393            element == EL_SATELLITE ||
7394            element == EL_PENGUIN ||
7395            element == EL_EMC_ANDROID)
7396   {
7397     int attr_x = -1, attr_y = -1;
7398
7399     if (AllPlayersGone)
7400     {
7401       attr_x = ExitX;
7402       attr_y = ExitY;
7403     }
7404     else
7405     {
7406       int i;
7407
7408       for (i = 0; i < MAX_PLAYERS; i++)
7409       {
7410         struct PlayerInfo *player = &stored_player[i];
7411         int jx = player->jx, jy = player->jy;
7412
7413         if (!player->active)
7414           continue;
7415
7416         if (attr_x == -1 ||
7417             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7418         {
7419           attr_x = jx;
7420           attr_y = jy;
7421         }
7422       }
7423     }
7424
7425     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7426         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7427          game.engine_version < VERSION_IDENT(3,1,0,0)))
7428     {
7429       attr_x = ZX;
7430       attr_y = ZY;
7431     }
7432
7433     if (element == EL_PENGUIN)
7434     {
7435       int i;
7436       static int xy[4][2] =
7437       {
7438         { 0, -1 },
7439         { -1, 0 },
7440         { +1, 0 },
7441         { 0, +1 }
7442       };
7443
7444       for (i = 0; i < NUM_DIRECTIONS; i++)
7445       {
7446         int ex = x + xy[i][0];
7447         int ey = y + xy[i][1];
7448
7449         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7450                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7451                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7452                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7453         {
7454           attr_x = ex;
7455           attr_y = ey;
7456           break;
7457         }
7458       }
7459     }
7460
7461     MovDir[x][y] = MV_NONE;
7462     if (attr_x < x)
7463       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7464     else if (attr_x > x)
7465       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7466     if (attr_y < y)
7467       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7468     else if (attr_y > y)
7469       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7470
7471     if (element == EL_ROBOT)
7472     {
7473       int newx, newy;
7474
7475       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7476         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7477       Moving2Blocked(x, y, &newx, &newy);
7478
7479       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7480         MovDelay[x][y] = 8 + 8 * !RND(3);
7481       else
7482         MovDelay[x][y] = 16;
7483     }
7484     else if (element == EL_PENGUIN)
7485     {
7486       int newx, newy;
7487
7488       MovDelay[x][y] = 1;
7489
7490       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7491       {
7492         boolean first_horiz = RND(2);
7493         int new_move_dir = MovDir[x][y];
7494
7495         MovDir[x][y] =
7496           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497         Moving2Blocked(x, y, &newx, &newy);
7498
7499         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7500           return;
7501
7502         MovDir[x][y] =
7503           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504         Moving2Blocked(x, y, &newx, &newy);
7505
7506         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7507           return;
7508
7509         MovDir[x][y] = old_move_dir;
7510         return;
7511       }
7512     }
7513     else if (element == EL_SATELLITE)
7514     {
7515       int newx, newy;
7516
7517       MovDelay[x][y] = 1;
7518
7519       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7520       {
7521         boolean first_horiz = RND(2);
7522         int new_move_dir = MovDir[x][y];
7523
7524         MovDir[x][y] =
7525           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7526         Moving2Blocked(x, y, &newx, &newy);
7527
7528         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7529           return;
7530
7531         MovDir[x][y] =
7532           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7533         Moving2Blocked(x, y, &newx, &newy);
7534
7535         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7536           return;
7537
7538         MovDir[x][y] = old_move_dir;
7539         return;
7540       }
7541     }
7542     else if (element == EL_EMC_ANDROID)
7543     {
7544       static int check_pos[16] =
7545       {
7546         -1,             /*  0 => (invalid)          */
7547         7,              /*  1 => MV_LEFT            */
7548         3,              /*  2 => MV_RIGHT           */
7549         -1,             /*  3 => (invalid)          */
7550         1,              /*  4 =>            MV_UP   */
7551         0,              /*  5 => MV_LEFT  | MV_UP   */
7552         2,              /*  6 => MV_RIGHT | MV_UP   */
7553         -1,             /*  7 => (invalid)          */
7554         5,              /*  8 =>            MV_DOWN */
7555         6,              /*  9 => MV_LEFT  | MV_DOWN */
7556         4,              /* 10 => MV_RIGHT | MV_DOWN */
7557         -1,             /* 11 => (invalid)          */
7558         -1,             /* 12 => (invalid)          */
7559         -1,             /* 13 => (invalid)          */
7560         -1,             /* 14 => (invalid)          */
7561         -1,             /* 15 => (invalid)          */
7562       };
7563       static struct
7564       {
7565         int dx, dy;
7566         int dir;
7567       } check_xy[8] =
7568       {
7569         { -1, -1,       MV_LEFT  | MV_UP   },
7570         {  0, -1,                  MV_UP   },
7571         { +1, -1,       MV_RIGHT | MV_UP   },
7572         { +1,  0,       MV_RIGHT           },
7573         { +1, +1,       MV_RIGHT | MV_DOWN },
7574         {  0, +1,                  MV_DOWN },
7575         { -1, +1,       MV_LEFT  | MV_DOWN },
7576         { -1,  0,       MV_LEFT            },
7577       };
7578       int start_pos, check_order;
7579       boolean can_clone = FALSE;
7580       int i;
7581
7582       /* check if there is any free field around current position */
7583       for (i = 0; i < 8; i++)
7584       {
7585         int newx = x + check_xy[i].dx;
7586         int newy = y + check_xy[i].dy;
7587
7588         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7589         {
7590           can_clone = TRUE;
7591
7592           break;
7593         }
7594       }
7595
7596       if (can_clone)            /* randomly find an element to clone */
7597       {
7598         can_clone = FALSE;
7599
7600         start_pos = check_pos[RND(8)];
7601         check_order = (RND(2) ? -1 : +1);
7602
7603         for (i = 0; i < 8; i++)
7604         {
7605           int pos_raw = start_pos + i * check_order;
7606           int pos = (pos_raw + 8) % 8;
7607           int newx = x + check_xy[pos].dx;
7608           int newy = y + check_xy[pos].dy;
7609
7610           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7611           {
7612             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7613             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7614
7615             Store[x][y] = Feld[newx][newy];
7616
7617             can_clone = TRUE;
7618
7619             break;
7620           }
7621         }
7622       }
7623
7624       if (can_clone)            /* randomly find a direction to move */
7625       {
7626         can_clone = FALSE;
7627
7628         start_pos = check_pos[RND(8)];
7629         check_order = (RND(2) ? -1 : +1);
7630
7631         for (i = 0; i < 8; i++)
7632         {
7633           int pos_raw = start_pos + i * check_order;
7634           int pos = (pos_raw + 8) % 8;
7635           int newx = x + check_xy[pos].dx;
7636           int newy = y + check_xy[pos].dy;
7637           int new_move_dir = check_xy[pos].dir;
7638
7639           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7640           {
7641             MovDir[x][y] = new_move_dir;
7642             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7643
7644             can_clone = TRUE;
7645
7646             break;
7647           }
7648         }
7649       }
7650
7651       if (can_clone)            /* cloning and moving successful */
7652         return;
7653
7654       /* cannot clone -- try to move towards player */
7655
7656       start_pos = check_pos[MovDir[x][y] & 0x0f];
7657       check_order = (RND(2) ? -1 : +1);
7658
7659       for (i = 0; i < 3; i++)
7660       {
7661         /* first check start_pos, then previous/next or (next/previous) pos */
7662         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7663         int pos = (pos_raw + 8) % 8;
7664         int newx = x + check_xy[pos].dx;
7665         int newy = y + check_xy[pos].dy;
7666         int new_move_dir = check_xy[pos].dir;
7667
7668         if (IS_PLAYER(newx, newy))
7669           break;
7670
7671         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7672         {
7673           MovDir[x][y] = new_move_dir;
7674           MovDelay[x][y] = level.android_move_time * 8 + 1;
7675
7676           break;
7677         }
7678       }
7679     }
7680   }
7681   else if (move_pattern == MV_TURNING_LEFT ||
7682            move_pattern == MV_TURNING_RIGHT ||
7683            move_pattern == MV_TURNING_LEFT_RIGHT ||
7684            move_pattern == MV_TURNING_RIGHT_LEFT ||
7685            move_pattern == MV_TURNING_RANDOM ||
7686            move_pattern == MV_ALL_DIRECTIONS)
7687   {
7688     boolean can_turn_left =
7689       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7690     boolean can_turn_right =
7691       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7692
7693     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7694       return;
7695
7696     if (move_pattern == MV_TURNING_LEFT)
7697       MovDir[x][y] = left_dir;
7698     else if (move_pattern == MV_TURNING_RIGHT)
7699       MovDir[x][y] = right_dir;
7700     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7701       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7702     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7703       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7704     else if (move_pattern == MV_TURNING_RANDOM)
7705       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7706                       can_turn_right && !can_turn_left ? right_dir :
7707                       RND(2) ? left_dir : right_dir);
7708     else if (can_turn_left && can_turn_right)
7709       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7710     else if (can_turn_left)
7711       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7712     else if (can_turn_right)
7713       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7714     else
7715       MovDir[x][y] = back_dir;
7716
7717     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7718   }
7719   else if (move_pattern == MV_HORIZONTAL ||
7720            move_pattern == MV_VERTICAL)
7721   {
7722     if (move_pattern & old_move_dir)
7723       MovDir[x][y] = back_dir;
7724     else if (move_pattern == MV_HORIZONTAL)
7725       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7726     else if (move_pattern == MV_VERTICAL)
7727       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7728
7729     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7730   }
7731   else if (move_pattern & MV_ANY_DIRECTION)
7732   {
7733     MovDir[x][y] = move_pattern;
7734     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7735   }
7736   else if (move_pattern & MV_WIND_DIRECTION)
7737   {
7738     MovDir[x][y] = game.wind_direction;
7739     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7740   }
7741   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7742   {
7743     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7744       MovDir[x][y] = left_dir;
7745     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7746       MovDir[x][y] = right_dir;
7747
7748     if (MovDir[x][y] != old_move_dir)
7749       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7752   {
7753     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7754       MovDir[x][y] = right_dir;
7755     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7756       MovDir[x][y] = left_dir;
7757
7758     if (MovDir[x][y] != old_move_dir)
7759       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7760   }
7761   else if (move_pattern == MV_TOWARDS_PLAYER ||
7762            move_pattern == MV_AWAY_FROM_PLAYER)
7763   {
7764     int attr_x = -1, attr_y = -1;
7765     int newx, newy;
7766     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7767
7768     if (AllPlayersGone)
7769     {
7770       attr_x = ExitX;
7771       attr_y = ExitY;
7772     }
7773     else
7774     {
7775       int i;
7776
7777       for (i = 0; i < MAX_PLAYERS; i++)
7778       {
7779         struct PlayerInfo *player = &stored_player[i];
7780         int jx = player->jx, jy = player->jy;
7781
7782         if (!player->active)
7783           continue;
7784
7785         if (attr_x == -1 ||
7786             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7787         {
7788           attr_x = jx;
7789           attr_y = jy;
7790         }
7791       }
7792     }
7793
7794     MovDir[x][y] = MV_NONE;
7795     if (attr_x < x)
7796       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7797     else if (attr_x > x)
7798       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7799     if (attr_y < y)
7800       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7801     else if (attr_y > y)
7802       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7803
7804     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7805
7806     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7807     {
7808       boolean first_horiz = RND(2);
7809       int new_move_dir = MovDir[x][y];
7810
7811       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7812       {
7813         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7814         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7815
7816         return;
7817       }
7818
7819       MovDir[x][y] =
7820         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7821       Moving2Blocked(x, y, &newx, &newy);
7822
7823       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7824         return;
7825
7826       MovDir[x][y] =
7827         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7828       Moving2Blocked(x, y, &newx, &newy);
7829
7830       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7831         return;
7832
7833       MovDir[x][y] = old_move_dir;
7834     }
7835   }
7836   else if (move_pattern == MV_WHEN_PUSHED ||
7837            move_pattern == MV_WHEN_DROPPED)
7838   {
7839     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7840       MovDir[x][y] = MV_NONE;
7841
7842     MovDelay[x][y] = 0;
7843   }
7844   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7845   {
7846     static int test_xy[7][2] =
7847     {
7848       { 0, -1 },
7849       { -1, 0 },
7850       { +1, 0 },
7851       { 0, +1 },
7852       { 0, -1 },
7853       { -1, 0 },
7854       { +1, 0 },
7855     };
7856     static int test_dir[7] =
7857     {
7858       MV_UP,
7859       MV_LEFT,
7860       MV_RIGHT,
7861       MV_DOWN,
7862       MV_UP,
7863       MV_LEFT,
7864       MV_RIGHT,
7865     };
7866     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7867     int move_preference = -1000000;     /* start with very low preference */
7868     int new_move_dir = MV_NONE;
7869     int start_test = RND(4);
7870     int i;
7871
7872     for (i = 0; i < NUM_DIRECTIONS; i++)
7873     {
7874       int move_dir = test_dir[start_test + i];
7875       int move_dir_preference;
7876
7877       xx = x + test_xy[start_test + i][0];
7878       yy = y + test_xy[start_test + i][1];
7879
7880       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7881           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7882       {
7883         new_move_dir = move_dir;
7884
7885         break;
7886       }
7887
7888       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7889         continue;
7890
7891       move_dir_preference = -1 * RunnerVisit[xx][yy];
7892       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7893         move_dir_preference = PlayerVisit[xx][yy];
7894
7895       if (move_dir_preference > move_preference)
7896       {
7897         /* prefer field that has not been visited for the longest time */
7898         move_preference = move_dir_preference;
7899         new_move_dir = move_dir;
7900       }
7901       else if (move_dir_preference == move_preference &&
7902                move_dir == old_move_dir)
7903       {
7904         /* prefer last direction when all directions are preferred equally */
7905         move_preference = move_dir_preference;
7906         new_move_dir = move_dir;
7907       }
7908     }
7909
7910     MovDir[x][y] = new_move_dir;
7911     if (old_move_dir != new_move_dir)
7912       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7913   }
7914 }
7915
7916 static void TurnRound(int x, int y)
7917 {
7918   int direction = MovDir[x][y];
7919
7920   TurnRoundExt(x, y);
7921
7922   GfxDir[x][y] = MovDir[x][y];
7923
7924   if (direction != MovDir[x][y])
7925     GfxFrame[x][y] = 0;
7926
7927   if (MovDelay[x][y])
7928     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7929
7930   ResetGfxFrame(x, y, FALSE);
7931 }
7932
7933 static boolean JustBeingPushed(int x, int y)
7934 {
7935   int i;
7936
7937   for (i = 0; i < MAX_PLAYERS; i++)
7938   {
7939     struct PlayerInfo *player = &stored_player[i];
7940
7941     if (player->active && player->is_pushing && player->MovPos)
7942     {
7943       int next_jx = player->jx + (player->jx - player->last_jx);
7944       int next_jy = player->jy + (player->jy - player->last_jy);
7945
7946       if (x == next_jx && y == next_jy)
7947         return TRUE;
7948     }
7949   }
7950
7951   return FALSE;
7952 }
7953
7954 void StartMoving(int x, int y)
7955 {
7956   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7957   int element = Feld[x][y];
7958
7959   if (Stop[x][y])
7960     return;
7961
7962   if (MovDelay[x][y] == 0)
7963     GfxAction[x][y] = ACTION_DEFAULT;
7964
7965   if (CAN_FALL(element) && y < lev_fieldy - 1)
7966   {
7967     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7968         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7969       if (JustBeingPushed(x, y))
7970         return;
7971
7972     if (element == EL_QUICKSAND_FULL)
7973     {
7974       if (IS_FREE(x, y + 1))
7975       {
7976         InitMovingField(x, y, MV_DOWN);
7977         started_moving = TRUE;
7978
7979         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7980 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7981         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7982           Store[x][y] = EL_ROCK;
7983 #else
7984         Store[x][y] = EL_ROCK;
7985 #endif
7986
7987         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7988       }
7989       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7990       {
7991         if (!MovDelay[x][y])
7992         {
7993           MovDelay[x][y] = TILEY + 1;
7994
7995           ResetGfxAnimation(x, y);
7996           ResetGfxAnimation(x, y + 1);
7997         }
7998
7999         if (MovDelay[x][y])
8000         {
8001           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8002           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8003
8004           MovDelay[x][y]--;
8005           if (MovDelay[x][y])
8006             return;
8007         }
8008
8009         Feld[x][y] = EL_QUICKSAND_EMPTY;
8010         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8011         Store[x][y + 1] = Store[x][y];
8012         Store[x][y] = 0;
8013
8014         PlayLevelSoundAction(x, y, ACTION_FILLING);
8015       }
8016       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8017       {
8018         if (!MovDelay[x][y])
8019         {
8020           MovDelay[x][y] = TILEY + 1;
8021
8022           ResetGfxAnimation(x, y);
8023           ResetGfxAnimation(x, y + 1);
8024         }
8025
8026         if (MovDelay[x][y])
8027         {
8028           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8029           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8030
8031           MovDelay[x][y]--;
8032           if (MovDelay[x][y])
8033             return;
8034         }
8035
8036         Feld[x][y] = EL_QUICKSAND_EMPTY;
8037         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8038         Store[x][y + 1] = Store[x][y];
8039         Store[x][y] = 0;
8040
8041         PlayLevelSoundAction(x, y, ACTION_FILLING);
8042       }
8043     }
8044     else if (element == EL_QUICKSAND_FAST_FULL)
8045     {
8046       if (IS_FREE(x, y + 1))
8047       {
8048         InitMovingField(x, y, MV_DOWN);
8049         started_moving = TRUE;
8050
8051         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8052 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8053         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8054           Store[x][y] = EL_ROCK;
8055 #else
8056         Store[x][y] = EL_ROCK;
8057 #endif
8058
8059         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8060       }
8061       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8062       {
8063         if (!MovDelay[x][y])
8064         {
8065           MovDelay[x][y] = TILEY + 1;
8066
8067           ResetGfxAnimation(x, y);
8068           ResetGfxAnimation(x, y + 1);
8069         }
8070
8071         if (MovDelay[x][y])
8072         {
8073           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8074           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8075
8076           MovDelay[x][y]--;
8077           if (MovDelay[x][y])
8078             return;
8079         }
8080
8081         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8082         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8083         Store[x][y + 1] = Store[x][y];
8084         Store[x][y] = 0;
8085
8086         PlayLevelSoundAction(x, y, ACTION_FILLING);
8087       }
8088       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8089       {
8090         if (!MovDelay[x][y])
8091         {
8092           MovDelay[x][y] = TILEY + 1;
8093
8094           ResetGfxAnimation(x, y);
8095           ResetGfxAnimation(x, y + 1);
8096         }
8097
8098         if (MovDelay[x][y])
8099         {
8100           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8101           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8102
8103           MovDelay[x][y]--;
8104           if (MovDelay[x][y])
8105             return;
8106         }
8107
8108         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8109         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8110         Store[x][y + 1] = Store[x][y];
8111         Store[x][y] = 0;
8112
8113         PlayLevelSoundAction(x, y, ACTION_FILLING);
8114       }
8115     }
8116     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8117              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8118     {
8119       InitMovingField(x, y, MV_DOWN);
8120       started_moving = TRUE;
8121
8122       Feld[x][y] = EL_QUICKSAND_FILLING;
8123       Store[x][y] = element;
8124
8125       PlayLevelSoundAction(x, y, ACTION_FILLING);
8126     }
8127     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8128              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8129     {
8130       InitMovingField(x, y, MV_DOWN);
8131       started_moving = TRUE;
8132
8133       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8134       Store[x][y] = element;
8135
8136       PlayLevelSoundAction(x, y, ACTION_FILLING);
8137     }
8138     else if (element == EL_MAGIC_WALL_FULL)
8139     {
8140       if (IS_FREE(x, y + 1))
8141       {
8142         InitMovingField(x, y, MV_DOWN);
8143         started_moving = TRUE;
8144
8145         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8146         Store[x][y] = EL_CHANGED(Store[x][y]);
8147       }
8148       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8149       {
8150         if (!MovDelay[x][y])
8151           MovDelay[x][y] = TILEY / 4 + 1;
8152
8153         if (MovDelay[x][y])
8154         {
8155           MovDelay[x][y]--;
8156           if (MovDelay[x][y])
8157             return;
8158         }
8159
8160         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8161         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8162         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8163         Store[x][y] = 0;
8164       }
8165     }
8166     else if (element == EL_BD_MAGIC_WALL_FULL)
8167     {
8168       if (IS_FREE(x, y + 1))
8169       {
8170         InitMovingField(x, y, MV_DOWN);
8171         started_moving = TRUE;
8172
8173         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8174         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8175       }
8176       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8177       {
8178         if (!MovDelay[x][y])
8179           MovDelay[x][y] = TILEY / 4 + 1;
8180
8181         if (MovDelay[x][y])
8182         {
8183           MovDelay[x][y]--;
8184           if (MovDelay[x][y])
8185             return;
8186         }
8187
8188         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8189         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8190         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8191         Store[x][y] = 0;
8192       }
8193     }
8194     else if (element == EL_DC_MAGIC_WALL_FULL)
8195     {
8196       if (IS_FREE(x, y + 1))
8197       {
8198         InitMovingField(x, y, MV_DOWN);
8199         started_moving = TRUE;
8200
8201         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8202         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8203       }
8204       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8205       {
8206         if (!MovDelay[x][y])
8207           MovDelay[x][y] = TILEY / 4 + 1;
8208
8209         if (MovDelay[x][y])
8210         {
8211           MovDelay[x][y]--;
8212           if (MovDelay[x][y])
8213             return;
8214         }
8215
8216         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8217         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8218         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8219         Store[x][y] = 0;
8220       }
8221     }
8222     else if ((CAN_PASS_MAGIC_WALL(element) &&
8223               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8224                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8225              (CAN_PASS_DC_MAGIC_WALL(element) &&
8226               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8227
8228     {
8229       InitMovingField(x, y, MV_DOWN);
8230       started_moving = TRUE;
8231
8232       Feld[x][y] =
8233         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8234          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8235          EL_DC_MAGIC_WALL_FILLING);
8236       Store[x][y] = element;
8237     }
8238     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8239     {
8240       SplashAcid(x, y + 1);
8241
8242       InitMovingField(x, y, MV_DOWN);
8243       started_moving = TRUE;
8244
8245       Store[x][y] = EL_ACID;
8246     }
8247     else if (
8248 #if USE_FIX_IMPACT_COLLISION
8249              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8250               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8251 #else
8252              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8253               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8254 #endif
8255              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8256               CAN_FALL(element) && WasJustFalling[x][y] &&
8257               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8258
8259              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8260               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8261               (Feld[x][y + 1] == EL_BLOCKED)))
8262     {
8263       /* this is needed for a special case not covered by calling "Impact()"
8264          from "ContinueMoving()": if an element moves to a tile directly below
8265          another element which was just falling on that tile (which was empty
8266          in the previous frame), the falling element above would just stop
8267          instead of smashing the element below (in previous version, the above
8268          element was just checked for "moving" instead of "falling", resulting
8269          in incorrect smashes caused by horizontal movement of the above
8270          element; also, the case of the player being the element to smash was
8271          simply not covered here... :-/ ) */
8272
8273       CheckCollision[x][y] = 0;
8274       CheckImpact[x][y] = 0;
8275
8276       Impact(x, y);
8277     }
8278     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8279     {
8280       if (MovDir[x][y] == MV_NONE)
8281       {
8282         InitMovingField(x, y, MV_DOWN);
8283         started_moving = TRUE;
8284       }
8285     }
8286     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8287     {
8288       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8289         MovDir[x][y] = MV_DOWN;
8290
8291       InitMovingField(x, y, MV_DOWN);
8292       started_moving = TRUE;
8293     }
8294     else if (element == EL_AMOEBA_DROP)
8295     {
8296       Feld[x][y] = EL_AMOEBA_GROWING;
8297       Store[x][y] = EL_AMOEBA_WET;
8298     }
8299     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8300               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8301              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8302              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8303     {
8304       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8305                                 (IS_FREE(x - 1, y + 1) ||
8306                                  Feld[x - 1][y + 1] == EL_ACID));
8307       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8308                                 (IS_FREE(x + 1, y + 1) ||
8309                                  Feld[x + 1][y + 1] == EL_ACID));
8310       boolean can_fall_any  = (can_fall_left || can_fall_right);
8311       boolean can_fall_both = (can_fall_left && can_fall_right);
8312       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8313
8314 #if USE_NEW_ALL_SLIPPERY
8315       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8316       {
8317         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8318           can_fall_right = FALSE;
8319         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8320           can_fall_left = FALSE;
8321         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8322           can_fall_right = FALSE;
8323         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8324           can_fall_left = FALSE;
8325
8326         can_fall_any  = (can_fall_left || can_fall_right);
8327         can_fall_both = FALSE;
8328       }
8329 #else
8330       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8331       {
8332         if (slippery_type == SLIPPERY_ONLY_LEFT)
8333           can_fall_right = FALSE;
8334         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8335           can_fall_left = FALSE;
8336         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8337           can_fall_right = FALSE;
8338         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8339           can_fall_left = FALSE;
8340
8341         can_fall_any  = (can_fall_left || can_fall_right);
8342         can_fall_both = (can_fall_left && can_fall_right);
8343       }
8344 #endif
8345
8346 #if USE_NEW_ALL_SLIPPERY
8347 #else
8348 #if USE_NEW_SP_SLIPPERY
8349       /* !!! better use the same properties as for custom elements here !!! */
8350       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8351                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8352       {
8353         can_fall_right = FALSE;         /* slip down on left side */
8354         can_fall_both = FALSE;
8355       }
8356 #endif
8357 #endif
8358
8359 #if USE_NEW_ALL_SLIPPERY
8360       if (can_fall_both)
8361       {
8362         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8363           can_fall_right = FALSE;       /* slip down on left side */
8364         else
8365           can_fall_left = !(can_fall_right = RND(2));
8366
8367         can_fall_both = FALSE;
8368       }
8369 #else
8370       if (can_fall_both)
8371       {
8372         if (game.emulation == EMU_BOULDERDASH ||
8373             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8374           can_fall_right = FALSE;       /* slip down on left side */
8375         else
8376           can_fall_left = !(can_fall_right = RND(2));
8377
8378         can_fall_both = FALSE;
8379       }
8380 #endif
8381
8382       if (can_fall_any)
8383       {
8384         /* if not determined otherwise, prefer left side for slipping down */
8385         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8386         started_moving = TRUE;
8387       }
8388     }
8389 #if 0
8390     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8391 #else
8392     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8393 #endif
8394     {
8395       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8396       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8397       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8398       int belt_dir = game.belt_dir[belt_nr];
8399
8400       if ((belt_dir == MV_LEFT  && left_is_free) ||
8401           (belt_dir == MV_RIGHT && right_is_free))
8402       {
8403         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8404
8405         InitMovingField(x, y, belt_dir);
8406         started_moving = TRUE;
8407
8408         Pushed[x][y] = TRUE;
8409         Pushed[nextx][y] = TRUE;
8410
8411         GfxAction[x][y] = ACTION_DEFAULT;
8412       }
8413       else
8414       {
8415         MovDir[x][y] = 0;       /* if element was moving, stop it */
8416       }
8417     }
8418   }
8419
8420   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8421 #if 0
8422   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8423 #else
8424   if (CAN_MOVE(element) && !started_moving)
8425 #endif
8426   {
8427     int move_pattern = element_info[element].move_pattern;
8428     int newx, newy;
8429
8430 #if 0
8431 #if DEBUG
8432     if (MovDir[x][y] == MV_NONE)
8433     {
8434       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8435              x, y, element, element_info[element].token_name);
8436       printf("StartMoving(): This should never happen!\n");
8437     }
8438 #endif
8439 #endif
8440
8441     Moving2Blocked(x, y, &newx, &newy);
8442
8443     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8444       return;
8445
8446     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8447         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8448     {
8449       WasJustMoving[x][y] = 0;
8450       CheckCollision[x][y] = 0;
8451
8452       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8453
8454       if (Feld[x][y] != element)        /* element has changed */
8455         return;
8456     }
8457
8458     if (!MovDelay[x][y])        /* start new movement phase */
8459     {
8460       /* all objects that can change their move direction after each step
8461          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8462
8463       if (element != EL_YAMYAM &&
8464           element != EL_DARK_YAMYAM &&
8465           element != EL_PACMAN &&
8466           !(move_pattern & MV_ANY_DIRECTION) &&
8467           move_pattern != MV_TURNING_LEFT &&
8468           move_pattern != MV_TURNING_RIGHT &&
8469           move_pattern != MV_TURNING_LEFT_RIGHT &&
8470           move_pattern != MV_TURNING_RIGHT_LEFT &&
8471           move_pattern != MV_TURNING_RANDOM)
8472       {
8473         TurnRound(x, y);
8474
8475         if (MovDelay[x][y] && (element == EL_BUG ||
8476                                element == EL_SPACESHIP ||
8477                                element == EL_SP_SNIKSNAK ||
8478                                element == EL_SP_ELECTRON ||
8479                                element == EL_MOLE))
8480           TEST_DrawLevelField(x, y);
8481       }
8482     }
8483
8484     if (MovDelay[x][y])         /* wait some time before next movement */
8485     {
8486       MovDelay[x][y]--;
8487
8488       if (element == EL_ROBOT ||
8489           element == EL_YAMYAM ||
8490           element == EL_DARK_YAMYAM)
8491       {
8492         DrawLevelElementAnimationIfNeeded(x, y, element);
8493         PlayLevelSoundAction(x, y, ACTION_WAITING);
8494       }
8495       else if (element == EL_SP_ELECTRON)
8496         DrawLevelElementAnimationIfNeeded(x, y, element);
8497       else if (element == EL_DRAGON)
8498       {
8499         int i;
8500         int dir = MovDir[x][y];
8501         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8502         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8503         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8504                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8505                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8506                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8507         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8508
8509         GfxAction[x][y] = ACTION_ATTACKING;
8510
8511         if (IS_PLAYER(x, y))
8512           DrawPlayerField(x, y);
8513         else
8514           TEST_DrawLevelField(x, y);
8515
8516         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8517
8518         for (i = 1; i <= 3; i++)
8519         {
8520           int xx = x + i * dx;
8521           int yy = y + i * dy;
8522           int sx = SCREENX(xx);
8523           int sy = SCREENY(yy);
8524           int flame_graphic = graphic + (i - 1);
8525
8526           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8527             break;
8528
8529           if (MovDelay[x][y])
8530           {
8531             int flamed = MovingOrBlocked2Element(xx, yy);
8532
8533             /* !!! */
8534 #if 0
8535             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8536               Bang(xx, yy);
8537             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8538               RemoveMovingField(xx, yy);
8539             else
8540               RemoveField(xx, yy);
8541 #else
8542             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8543               Bang(xx, yy);
8544             else
8545               RemoveMovingField(xx, yy);
8546 #endif
8547
8548             ChangeDelay[xx][yy] = 0;
8549
8550             Feld[xx][yy] = EL_FLAMES;
8551
8552             if (IN_SCR_FIELD(sx, sy))
8553             {
8554               TEST_DrawLevelFieldCrumbled(xx, yy);
8555               DrawGraphic(sx, sy, flame_graphic, frame);
8556             }
8557           }
8558           else
8559           {
8560             if (Feld[xx][yy] == EL_FLAMES)
8561               Feld[xx][yy] = EL_EMPTY;
8562             TEST_DrawLevelField(xx, yy);
8563           }
8564         }
8565       }
8566
8567       if (MovDelay[x][y])       /* element still has to wait some time */
8568       {
8569         PlayLevelSoundAction(x, y, ACTION_WAITING);
8570
8571         return;
8572       }
8573     }
8574
8575     /* now make next step */
8576
8577     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8578
8579     if (DONT_COLLIDE_WITH(element) &&
8580         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8581         !PLAYER_ENEMY_PROTECTED(newx, newy))
8582     {
8583       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8584
8585       return;
8586     }
8587
8588     else if (CAN_MOVE_INTO_ACID(element) &&
8589              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8590              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8591              (MovDir[x][y] == MV_DOWN ||
8592               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8593     {
8594       SplashAcid(newx, newy);
8595       Store[x][y] = EL_ACID;
8596     }
8597     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8598     {
8599       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8600           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8601           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8602           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8603       {
8604         RemoveField(x, y);
8605         TEST_DrawLevelField(x, y);
8606
8607         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8608         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8609           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8610
8611         local_player->friends_still_needed--;
8612         if (!local_player->friends_still_needed &&
8613             !local_player->GameOver && AllPlayersGone)
8614           PlayerWins(local_player);
8615
8616         return;
8617       }
8618       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8619       {
8620         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8621           TEST_DrawLevelField(newx, newy);
8622         else
8623           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8624       }
8625       else if (!IS_FREE(newx, newy))
8626       {
8627         GfxAction[x][y] = ACTION_WAITING;
8628
8629         if (IS_PLAYER(x, y))
8630           DrawPlayerField(x, y);
8631         else
8632           TEST_DrawLevelField(x, y);
8633
8634         return;
8635       }
8636     }
8637     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8638     {
8639       if (IS_FOOD_PIG(Feld[newx][newy]))
8640       {
8641         if (IS_MOVING(newx, newy))
8642           RemoveMovingField(newx, newy);
8643         else
8644         {
8645           Feld[newx][newy] = EL_EMPTY;
8646           TEST_DrawLevelField(newx, newy);
8647         }
8648
8649         PlayLevelSound(x, y, SND_PIG_DIGGING);
8650       }
8651       else if (!IS_FREE(newx, newy))
8652       {
8653         if (IS_PLAYER(x, y))
8654           DrawPlayerField(x, y);
8655         else
8656           TEST_DrawLevelField(x, y);
8657
8658         return;
8659       }
8660     }
8661     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8662     {
8663       if (Store[x][y] != EL_EMPTY)
8664       {
8665         boolean can_clone = FALSE;
8666         int xx, yy;
8667
8668         /* check if element to clone is still there */
8669         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8670         {
8671           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8672           {
8673             can_clone = TRUE;
8674
8675             break;
8676           }
8677         }
8678
8679         /* cannot clone or target field not free anymore -- do not clone */
8680         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8681           Store[x][y] = EL_EMPTY;
8682       }
8683
8684       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8685       {
8686         if (IS_MV_DIAGONAL(MovDir[x][y]))
8687         {
8688           int diagonal_move_dir = MovDir[x][y];
8689           int stored = Store[x][y];
8690           int change_delay = 8;
8691           int graphic;
8692
8693           /* android is moving diagonally */
8694
8695           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8696
8697           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8698           GfxElement[x][y] = EL_EMC_ANDROID;
8699           GfxAction[x][y] = ACTION_SHRINKING;
8700           GfxDir[x][y] = diagonal_move_dir;
8701           ChangeDelay[x][y] = change_delay;
8702
8703           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8704                                    GfxDir[x][y]);
8705
8706           DrawLevelGraphicAnimation(x, y, graphic);
8707           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8708
8709           if (Feld[newx][newy] == EL_ACID)
8710           {
8711             SplashAcid(newx, newy);
8712
8713             return;
8714           }
8715
8716           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8717
8718           Store[newx][newy] = EL_EMC_ANDROID;
8719           GfxElement[newx][newy] = EL_EMC_ANDROID;
8720           GfxAction[newx][newy] = ACTION_GROWING;
8721           GfxDir[newx][newy] = diagonal_move_dir;
8722           ChangeDelay[newx][newy] = change_delay;
8723
8724           graphic = el_act_dir2img(GfxElement[newx][newy],
8725                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8726
8727           DrawLevelGraphicAnimation(newx, newy, graphic);
8728           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8729
8730           return;
8731         }
8732         else
8733         {
8734           Feld[newx][newy] = EL_EMPTY;
8735           TEST_DrawLevelField(newx, newy);
8736
8737           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8738         }
8739       }
8740       else if (!IS_FREE(newx, newy))
8741       {
8742 #if 0
8743         if (IS_PLAYER(x, y))
8744           DrawPlayerField(x, y);
8745         else
8746           TEST_DrawLevelField(x, y);
8747 #endif
8748
8749         return;
8750       }
8751     }
8752     else if (IS_CUSTOM_ELEMENT(element) &&
8753              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8754     {
8755 #if 1
8756       if (!DigFieldByCE(newx, newy, element))
8757         return;
8758 #else
8759       int new_element = Feld[newx][newy];
8760
8761       if (!IS_FREE(newx, newy))
8762       {
8763         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8764                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8765                       ACTION_BREAKING);
8766
8767         /* no element can dig solid indestructible elements */
8768         if (IS_INDESTRUCTIBLE(new_element) &&
8769             !IS_DIGGABLE(new_element) &&
8770             !IS_COLLECTIBLE(new_element))
8771           return;
8772
8773         if (AmoebaNr[newx][newy] &&
8774             (new_element == EL_AMOEBA_FULL ||
8775              new_element == EL_BD_AMOEBA ||
8776              new_element == EL_AMOEBA_GROWING))
8777         {
8778           AmoebaCnt[AmoebaNr[newx][newy]]--;
8779           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8780         }
8781
8782         if (IS_MOVING(newx, newy))
8783           RemoveMovingField(newx, newy);
8784         else
8785         {
8786           RemoveField(newx, newy);
8787           TEST_DrawLevelField(newx, newy);
8788         }
8789
8790         /* if digged element was about to explode, prevent the explosion */
8791         ExplodeField[newx][newy] = EX_TYPE_NONE;
8792
8793         PlayLevelSoundAction(x, y, action);
8794       }
8795
8796       Store[newx][newy] = EL_EMPTY;
8797
8798 #if 1
8799       /* this makes it possible to leave the removed element again */
8800       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8801         Store[newx][newy] = new_element;
8802 #else
8803       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8804       {
8805         int move_leave_element = element_info[element].move_leave_element;
8806
8807         /* this makes it possible to leave the removed element again */
8808         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8809                              new_element : move_leave_element);
8810       }
8811 #endif
8812
8813 #endif
8814
8815       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8816       {
8817         RunnerVisit[x][y] = FrameCounter;
8818         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8819       }
8820     }
8821     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8822     {
8823       if (!IS_FREE(newx, newy))
8824       {
8825         if (IS_PLAYER(x, y))
8826           DrawPlayerField(x, y);
8827         else
8828           TEST_DrawLevelField(x, y);
8829
8830         return;
8831       }
8832       else
8833       {
8834         boolean wanna_flame = !RND(10);
8835         int dx = newx - x, dy = newy - y;
8836         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8837         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8838         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8839                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8840         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8841                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8842
8843         if ((wanna_flame ||
8844              IS_CLASSIC_ENEMY(element1) ||
8845              IS_CLASSIC_ENEMY(element2)) &&
8846             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8847             element1 != EL_FLAMES && element2 != EL_FLAMES)
8848         {
8849           ResetGfxAnimation(x, y);
8850           GfxAction[x][y] = ACTION_ATTACKING;
8851
8852           if (IS_PLAYER(x, y))
8853             DrawPlayerField(x, y);
8854           else
8855             TEST_DrawLevelField(x, y);
8856
8857           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8858
8859           MovDelay[x][y] = 50;
8860
8861           /* !!! */
8862 #if 0
8863           RemoveField(newx, newy);
8864 #endif
8865           Feld[newx][newy] = EL_FLAMES;
8866           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8867           {
8868 #if 0
8869             RemoveField(newx1, newy1);
8870 #endif
8871             Feld[newx1][newy1] = EL_FLAMES;
8872           }
8873           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8874           {
8875 #if 0
8876             RemoveField(newx2, newy2);
8877 #endif
8878             Feld[newx2][newy2] = EL_FLAMES;
8879           }
8880
8881           return;
8882         }
8883       }
8884     }
8885     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8886              Feld[newx][newy] == EL_DIAMOND)
8887     {
8888       if (IS_MOVING(newx, newy))
8889         RemoveMovingField(newx, newy);
8890       else
8891       {
8892         Feld[newx][newy] = EL_EMPTY;
8893         TEST_DrawLevelField(newx, newy);
8894       }
8895
8896       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8897     }
8898     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8899              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8900     {
8901       if (AmoebaNr[newx][newy])
8902       {
8903         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8904         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8905             Feld[newx][newy] == EL_BD_AMOEBA)
8906           AmoebaCnt[AmoebaNr[newx][newy]]--;
8907       }
8908
8909 #if 0
8910       /* !!! test !!! */
8911       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8912       {
8913         RemoveMovingField(newx, newy);
8914       }
8915 #else
8916       if (IS_MOVING(newx, newy))
8917       {
8918         RemoveMovingField(newx, newy);
8919       }
8920 #endif
8921       else
8922       {
8923         Feld[newx][newy] = EL_EMPTY;
8924         TEST_DrawLevelField(newx, newy);
8925       }
8926
8927       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8928     }
8929     else if ((element == EL_PACMAN || element == EL_MOLE)
8930              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8931     {
8932       if (AmoebaNr[newx][newy])
8933       {
8934         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8935         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8936             Feld[newx][newy] == EL_BD_AMOEBA)
8937           AmoebaCnt[AmoebaNr[newx][newy]]--;
8938       }
8939
8940       if (element == EL_MOLE)
8941       {
8942         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8943         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8944
8945         ResetGfxAnimation(x, y);
8946         GfxAction[x][y] = ACTION_DIGGING;
8947         TEST_DrawLevelField(x, y);
8948
8949         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8950
8951         return;                         /* wait for shrinking amoeba */
8952       }
8953       else      /* element == EL_PACMAN */
8954       {
8955         Feld[newx][newy] = EL_EMPTY;
8956         TEST_DrawLevelField(newx, newy);
8957         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8958       }
8959     }
8960     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8961              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8962               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8963     {
8964       /* wait for shrinking amoeba to completely disappear */
8965       return;
8966     }
8967     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8968     {
8969       /* object was running against a wall */
8970
8971       TurnRound(x, y);
8972
8973 #if 0
8974       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8975       if (move_pattern & MV_ANY_DIRECTION &&
8976           move_pattern == MovDir[x][y])
8977       {
8978         int blocking_element =
8979           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8980
8981         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8982                                  MovDir[x][y]);
8983
8984         element = Feld[x][y];   /* element might have changed */
8985       }
8986 #endif
8987
8988       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8989         DrawLevelElementAnimation(x, y, element);
8990
8991       if (DONT_TOUCH(element))
8992         TestIfBadThingTouchesPlayer(x, y);
8993
8994       return;
8995     }
8996
8997     InitMovingField(x, y, MovDir[x][y]);
8998
8999     PlayLevelSoundAction(x, y, ACTION_MOVING);
9000   }
9001
9002   if (MovDir[x][y])
9003     ContinueMoving(x, y);
9004 }
9005
9006 void ContinueMoving(int x, int y)
9007 {
9008   int element = Feld[x][y];
9009   struct ElementInfo *ei = &element_info[element];
9010   int direction = MovDir[x][y];
9011   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9012   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9013   int newx = x + dx, newy = y + dy;
9014   int stored = Store[x][y];
9015   int stored_new = Store[newx][newy];
9016   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9017   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9018   boolean last_line = (newy == lev_fieldy - 1);
9019
9020   MovPos[x][y] += getElementMoveStepsize(x, y);
9021
9022   if (pushed_by_player) /* special case: moving object pushed by player */
9023     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9024
9025   if (ABS(MovPos[x][y]) < TILEX)
9026   {
9027 #if 0
9028     int ee = Feld[x][y];
9029     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9030     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9031
9032     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9033            x, y, ABS(MovPos[x][y]),
9034            ee, gg, ff,
9035            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9036 #endif
9037
9038     TEST_DrawLevelField(x, y);
9039
9040     return;     /* element is still moving */
9041   }
9042
9043   /* element reached destination field */
9044
9045   Feld[x][y] = EL_EMPTY;
9046   Feld[newx][newy] = element;
9047   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9048
9049   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9050   {
9051     element = Feld[newx][newy] = EL_ACID;
9052   }
9053   else if (element == EL_MOLE)
9054   {
9055     Feld[x][y] = EL_SAND;
9056
9057     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9058   }
9059   else if (element == EL_QUICKSAND_FILLING)
9060   {
9061     element = Feld[newx][newy] = get_next_element(element);
9062     Store[newx][newy] = Store[x][y];
9063   }
9064   else if (element == EL_QUICKSAND_EMPTYING)
9065   {
9066     Feld[x][y] = get_next_element(element);
9067     element = Feld[newx][newy] = Store[x][y];
9068   }
9069   else if (element == EL_QUICKSAND_FAST_FILLING)
9070   {
9071     element = Feld[newx][newy] = get_next_element(element);
9072     Store[newx][newy] = Store[x][y];
9073   }
9074   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9075   {
9076     Feld[x][y] = get_next_element(element);
9077     element = Feld[newx][newy] = Store[x][y];
9078   }
9079   else if (element == EL_MAGIC_WALL_FILLING)
9080   {
9081     element = Feld[newx][newy] = get_next_element(element);
9082     if (!game.magic_wall_active)
9083       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9084     Store[newx][newy] = Store[x][y];
9085   }
9086   else if (element == EL_MAGIC_WALL_EMPTYING)
9087   {
9088     Feld[x][y] = get_next_element(element);
9089     if (!game.magic_wall_active)
9090       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9091     element = Feld[newx][newy] = Store[x][y];
9092
9093 #if USE_NEW_CUSTOM_VALUE
9094     InitField(newx, newy, FALSE);
9095 #endif
9096   }
9097   else if (element == EL_BD_MAGIC_WALL_FILLING)
9098   {
9099     element = Feld[newx][newy] = get_next_element(element);
9100     if (!game.magic_wall_active)
9101       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9102     Store[newx][newy] = Store[x][y];
9103   }
9104   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9105   {
9106     Feld[x][y] = get_next_element(element);
9107     if (!game.magic_wall_active)
9108       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9109     element = Feld[newx][newy] = Store[x][y];
9110
9111 #if USE_NEW_CUSTOM_VALUE
9112     InitField(newx, newy, FALSE);
9113 #endif
9114   }
9115   else if (element == EL_DC_MAGIC_WALL_FILLING)
9116   {
9117     element = Feld[newx][newy] = get_next_element(element);
9118     if (!game.magic_wall_active)
9119       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9120     Store[newx][newy] = Store[x][y];
9121   }
9122   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9123   {
9124     Feld[x][y] = get_next_element(element);
9125     if (!game.magic_wall_active)
9126       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9127     element = Feld[newx][newy] = Store[x][y];
9128
9129 #if USE_NEW_CUSTOM_VALUE
9130     InitField(newx, newy, FALSE);
9131 #endif
9132   }
9133   else if (element == EL_AMOEBA_DROPPING)
9134   {
9135     Feld[x][y] = get_next_element(element);
9136     element = Feld[newx][newy] = Store[x][y];
9137   }
9138   else if (element == EL_SOKOBAN_OBJECT)
9139   {
9140     if (Back[x][y])
9141       Feld[x][y] = Back[x][y];
9142
9143     if (Back[newx][newy])
9144       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9145
9146     Back[x][y] = Back[newx][newy] = 0;
9147   }
9148
9149   Store[x][y] = EL_EMPTY;
9150   MovPos[x][y] = 0;
9151   MovDir[x][y] = 0;
9152   MovDelay[x][y] = 0;
9153
9154   MovDelay[newx][newy] = 0;
9155
9156   if (CAN_CHANGE_OR_HAS_ACTION(element))
9157   {
9158     /* copy element change control values to new field */
9159     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9160     ChangePage[newx][newy]  = ChangePage[x][y];
9161     ChangeCount[newx][newy] = ChangeCount[x][y];
9162     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9163   }
9164
9165 #if USE_NEW_CUSTOM_VALUE
9166   CustomValue[newx][newy] = CustomValue[x][y];
9167 #endif
9168
9169   ChangeDelay[x][y] = 0;
9170   ChangePage[x][y] = -1;
9171   ChangeCount[x][y] = 0;
9172   ChangeEvent[x][y] = -1;
9173
9174 #if USE_NEW_CUSTOM_VALUE
9175   CustomValue[x][y] = 0;
9176 #endif
9177
9178   /* copy animation control values to new field */
9179   GfxFrame[newx][newy]  = GfxFrame[x][y];
9180   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9181   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9182   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9183
9184   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9185
9186   /* some elements can leave other elements behind after moving */
9187 #if 1
9188   if (ei->move_leave_element != EL_EMPTY &&
9189       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9190       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9191 #else
9192   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9193       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9194       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9195 #endif
9196   {
9197     int move_leave_element = ei->move_leave_element;
9198
9199 #if 1
9200 #if 1
9201     /* this makes it possible to leave the removed element again */
9202     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9204 #else
9205     /* this makes it possible to leave the removed element again */
9206     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9207       move_leave_element = stored;
9208 #endif
9209 #else
9210     /* this makes it possible to leave the removed element again */
9211     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9212         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9213       move_leave_element = stored;
9214 #endif
9215
9216     Feld[x][y] = move_leave_element;
9217
9218     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9219       MovDir[x][y] = direction;
9220
9221     InitField(x, y, FALSE);
9222
9223     if (GFX_CRUMBLED(Feld[x][y]))
9224       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9225
9226     if (ELEM_IS_PLAYER(move_leave_element))
9227       RelocatePlayer(x, y, move_leave_element);
9228   }
9229
9230   /* do this after checking for left-behind element */
9231   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9232
9233   if (!CAN_MOVE(element) ||
9234       (CAN_FALL(element) && direction == MV_DOWN &&
9235        (element == EL_SPRING ||
9236         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9237         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9238     GfxDir[x][y] = MovDir[newx][newy] = 0;
9239
9240   TEST_DrawLevelField(x, y);
9241   TEST_DrawLevelField(newx, newy);
9242
9243   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9244
9245   /* prevent pushed element from moving on in pushed direction */
9246   if (pushed_by_player && CAN_MOVE(element) &&
9247       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9248       !(element_info[element].move_pattern & direction))
9249     TurnRound(newx, newy);
9250
9251   /* prevent elements on conveyor belt from moving on in last direction */
9252   if (pushed_by_conveyor && CAN_FALL(element) &&
9253       direction & MV_HORIZONTAL)
9254     MovDir[newx][newy] = 0;
9255
9256   if (!pushed_by_player)
9257   {
9258     int nextx = newx + dx, nexty = newy + dy;
9259     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9260
9261     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9262
9263     if (CAN_FALL(element) && direction == MV_DOWN)
9264       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9265
9266     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9267       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9268
9269 #if USE_FIX_IMPACT_COLLISION
9270     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9271       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9272 #endif
9273   }
9274
9275   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9276   {
9277     TestIfBadThingTouchesPlayer(newx, newy);
9278     TestIfBadThingTouchesFriend(newx, newy);
9279
9280     if (!IS_CUSTOM_ELEMENT(element))
9281       TestIfBadThingTouchesOtherBadThing(newx, newy);
9282   }
9283   else if (element == EL_PENGUIN)
9284     TestIfFriendTouchesBadThing(newx, newy);
9285
9286   if (DONT_GET_HIT_BY(element))
9287   {
9288     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9289   }
9290
9291   /* give the player one last chance (one more frame) to move away */
9292   if (CAN_FALL(element) && direction == MV_DOWN &&
9293       (last_line || (!IS_FREE(x, newy + 1) &&
9294                      (!IS_PLAYER(x, newy + 1) ||
9295                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9296     Impact(x, newy);
9297
9298   if (pushed_by_player && !game.use_change_when_pushing_bug)
9299   {
9300     int push_side = MV_DIR_OPPOSITE(direction);
9301     struct PlayerInfo *player = PLAYERINFO(x, y);
9302
9303     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9304                                player->index_bit, push_side);
9305     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9306                                         player->index_bit, push_side);
9307   }
9308
9309   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9310     MovDelay[newx][newy] = 1;
9311
9312   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9313
9314   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9315
9316 #if 0
9317   if (ChangePage[newx][newy] != -1)             /* delayed change */
9318   {
9319     int page = ChangePage[newx][newy];
9320     struct ElementChangeInfo *change = &ei->change_page[page];
9321
9322     ChangePage[newx][newy] = -1;
9323
9324     if (change->can_change)
9325     {
9326       if (ChangeElement(newx, newy, element, page))
9327       {
9328         if (change->post_change_function)
9329           change->post_change_function(newx, newy);
9330       }
9331     }
9332
9333     if (change->has_action)
9334       ExecuteCustomElementAction(newx, newy, element, page);
9335   }
9336 #endif
9337
9338   TestIfElementHitsCustomElement(newx, newy, direction);
9339   TestIfPlayerTouchesCustomElement(newx, newy);
9340   TestIfElementTouchesCustomElement(newx, newy);
9341
9342   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9343       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9344     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9345                              MV_DIR_OPPOSITE(direction));
9346 }
9347
9348 int AmoebeNachbarNr(int ax, int ay)
9349 {
9350   int i;
9351   int element = Feld[ax][ay];
9352   int group_nr = 0;
9353   static int xy[4][2] =
9354   {
9355     { 0, -1 },
9356     { -1, 0 },
9357     { +1, 0 },
9358     { 0, +1 }
9359   };
9360
9361   for (i = 0; i < NUM_DIRECTIONS; i++)
9362   {
9363     int x = ax + xy[i][0];
9364     int y = ay + xy[i][1];
9365
9366     if (!IN_LEV_FIELD(x, y))
9367       continue;
9368
9369     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9370       group_nr = AmoebaNr[x][y];
9371   }
9372
9373   return group_nr;
9374 }
9375
9376 void AmoebenVereinigen(int ax, int ay)
9377 {
9378   int i, x, y, xx, yy;
9379   int new_group_nr = AmoebaNr[ax][ay];
9380   static int xy[4][2] =
9381   {
9382     { 0, -1 },
9383     { -1, 0 },
9384     { +1, 0 },
9385     { 0, +1 }
9386   };
9387
9388   if (new_group_nr == 0)
9389     return;
9390
9391   for (i = 0; i < NUM_DIRECTIONS; i++)
9392   {
9393     x = ax + xy[i][0];
9394     y = ay + xy[i][1];
9395
9396     if (!IN_LEV_FIELD(x, y))
9397       continue;
9398
9399     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9400          Feld[x][y] == EL_BD_AMOEBA ||
9401          Feld[x][y] == EL_AMOEBA_DEAD) &&
9402         AmoebaNr[x][y] != new_group_nr)
9403     {
9404       int old_group_nr = AmoebaNr[x][y];
9405
9406       if (old_group_nr == 0)
9407         return;
9408
9409       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9410       AmoebaCnt[old_group_nr] = 0;
9411       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9412       AmoebaCnt2[old_group_nr] = 0;
9413
9414       SCAN_PLAYFIELD(xx, yy)
9415       {
9416         if (AmoebaNr[xx][yy] == old_group_nr)
9417           AmoebaNr[xx][yy] = new_group_nr;
9418       }
9419     }
9420   }
9421 }
9422
9423 void AmoebeUmwandeln(int ax, int ay)
9424 {
9425   int i, x, y;
9426
9427   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9428   {
9429     int group_nr = AmoebaNr[ax][ay];
9430
9431 #ifdef DEBUG
9432     if (group_nr == 0)
9433     {
9434       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9435       printf("AmoebeUmwandeln(): This should never happen!\n");
9436       return;
9437     }
9438 #endif
9439
9440     SCAN_PLAYFIELD(x, y)
9441     {
9442       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9443       {
9444         AmoebaNr[x][y] = 0;
9445         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9446       }
9447     }
9448
9449     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9450                             SND_AMOEBA_TURNING_TO_GEM :
9451                             SND_AMOEBA_TURNING_TO_ROCK));
9452     Bang(ax, ay);
9453   }
9454   else
9455   {
9456     static int xy[4][2] =
9457     {
9458       { 0, -1 },
9459       { -1, 0 },
9460       { +1, 0 },
9461       { 0, +1 }
9462     };
9463
9464     for (i = 0; i < NUM_DIRECTIONS; i++)
9465     {
9466       x = ax + xy[i][0];
9467       y = ay + xy[i][1];
9468
9469       if (!IN_LEV_FIELD(x, y))
9470         continue;
9471
9472       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9473       {
9474         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9475                               SND_AMOEBA_TURNING_TO_GEM :
9476                               SND_AMOEBA_TURNING_TO_ROCK));
9477         Bang(x, y);
9478       }
9479     }
9480   }
9481 }
9482
9483 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9484 {
9485   int x, y;
9486   int group_nr = AmoebaNr[ax][ay];
9487   boolean done = FALSE;
9488
9489 #ifdef DEBUG
9490   if (group_nr == 0)
9491   {
9492     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9493     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9494     return;
9495   }
9496 #endif
9497
9498   SCAN_PLAYFIELD(x, y)
9499   {
9500     if (AmoebaNr[x][y] == group_nr &&
9501         (Feld[x][y] == EL_AMOEBA_DEAD ||
9502          Feld[x][y] == EL_BD_AMOEBA ||
9503          Feld[x][y] == EL_AMOEBA_GROWING))
9504     {
9505       AmoebaNr[x][y] = 0;
9506       Feld[x][y] = new_element;
9507       InitField(x, y, FALSE);
9508       TEST_DrawLevelField(x, y);
9509       done = TRUE;
9510     }
9511   }
9512
9513   if (done)
9514     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9515                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9516                             SND_BD_AMOEBA_TURNING_TO_GEM));
9517 }
9518
9519 void AmoebeWaechst(int x, int y)
9520 {
9521   static unsigned long sound_delay = 0;
9522   static unsigned long sound_delay_value = 0;
9523
9524   if (!MovDelay[x][y])          /* start new growing cycle */
9525   {
9526     MovDelay[x][y] = 7;
9527
9528     if (DelayReached(&sound_delay, sound_delay_value))
9529     {
9530       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9531       sound_delay_value = 30;
9532     }
9533   }
9534
9535   if (MovDelay[x][y])           /* wait some time before growing bigger */
9536   {
9537     MovDelay[x][y]--;
9538     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9539     {
9540       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9541                                            6 - MovDelay[x][y]);
9542
9543       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9544     }
9545
9546     if (!MovDelay[x][y])
9547     {
9548       Feld[x][y] = Store[x][y];
9549       Store[x][y] = 0;
9550       TEST_DrawLevelField(x, y);
9551     }
9552   }
9553 }
9554
9555 void AmoebaDisappearing(int x, int y)
9556 {
9557   static unsigned long sound_delay = 0;
9558   static unsigned long sound_delay_value = 0;
9559
9560   if (!MovDelay[x][y])          /* start new shrinking cycle */
9561   {
9562     MovDelay[x][y] = 7;
9563
9564     if (DelayReached(&sound_delay, sound_delay_value))
9565       sound_delay_value = 30;
9566   }
9567
9568   if (MovDelay[x][y])           /* wait some time before shrinking */
9569   {
9570     MovDelay[x][y]--;
9571     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9572     {
9573       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9574                                            6 - MovDelay[x][y]);
9575
9576       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9577     }
9578
9579     if (!MovDelay[x][y])
9580     {
9581       Feld[x][y] = EL_EMPTY;
9582       TEST_DrawLevelField(x, y);
9583
9584       /* don't let mole enter this field in this cycle;
9585          (give priority to objects falling to this field from above) */
9586       Stop[x][y] = TRUE;
9587     }
9588   }
9589 }
9590
9591 void AmoebeAbleger(int ax, int ay)
9592 {
9593   int i;
9594   int element = Feld[ax][ay];
9595   int graphic = el2img(element);
9596   int newax = ax, neway = ay;
9597   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9598   static int xy[4][2] =
9599   {
9600     { 0, -1 },
9601     { -1, 0 },
9602     { +1, 0 },
9603     { 0, +1 }
9604   };
9605
9606   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9607   {
9608     Feld[ax][ay] = EL_AMOEBA_DEAD;
9609     TEST_DrawLevelField(ax, ay);
9610     return;
9611   }
9612
9613   if (IS_ANIMATED(graphic))
9614     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9615
9616   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9617     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9618
9619   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9620   {
9621     MovDelay[ax][ay]--;
9622     if (MovDelay[ax][ay])
9623       return;
9624   }
9625
9626   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9627   {
9628     int start = RND(4);
9629     int x = ax + xy[start][0];
9630     int y = ay + xy[start][1];
9631
9632     if (!IN_LEV_FIELD(x, y))
9633       return;
9634
9635     if (IS_FREE(x, y) ||
9636         CAN_GROW_INTO(Feld[x][y]) ||
9637         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9638         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9639     {
9640       newax = x;
9641       neway = y;
9642     }
9643
9644     if (newax == ax && neway == ay)
9645       return;
9646   }
9647   else                          /* normal or "filled" (BD style) amoeba */
9648   {
9649     int start = RND(4);
9650     boolean waiting_for_player = FALSE;
9651
9652     for (i = 0; i < NUM_DIRECTIONS; i++)
9653     {
9654       int j = (start + i) % 4;
9655       int x = ax + xy[j][0];
9656       int y = ay + xy[j][1];
9657
9658       if (!IN_LEV_FIELD(x, y))
9659         continue;
9660
9661       if (IS_FREE(x, y) ||
9662           CAN_GROW_INTO(Feld[x][y]) ||
9663           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9664           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9665       {
9666         newax = x;
9667         neway = y;
9668         break;
9669       }
9670       else if (IS_PLAYER(x, y))
9671         waiting_for_player = TRUE;
9672     }
9673
9674     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9675     {
9676       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9677       {
9678         Feld[ax][ay] = EL_AMOEBA_DEAD;
9679         TEST_DrawLevelField(ax, ay);
9680         AmoebaCnt[AmoebaNr[ax][ay]]--;
9681
9682         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9683         {
9684           if (element == EL_AMOEBA_FULL)
9685             AmoebeUmwandeln(ax, ay);
9686           else if (element == EL_BD_AMOEBA)
9687             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9688         }
9689       }
9690       return;
9691     }
9692     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9693     {
9694       /* amoeba gets larger by growing in some direction */
9695
9696       int new_group_nr = AmoebaNr[ax][ay];
9697
9698 #ifdef DEBUG
9699   if (new_group_nr == 0)
9700   {
9701     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9702     printf("AmoebeAbleger(): This should never happen!\n");
9703     return;
9704   }
9705 #endif
9706
9707       AmoebaNr[newax][neway] = new_group_nr;
9708       AmoebaCnt[new_group_nr]++;
9709       AmoebaCnt2[new_group_nr]++;
9710
9711       /* if amoeba touches other amoeba(s) after growing, unify them */
9712       AmoebenVereinigen(newax, neway);
9713
9714       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9715       {
9716         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9717         return;
9718       }
9719     }
9720   }
9721
9722   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9723       (neway == lev_fieldy - 1 && newax != ax))
9724   {
9725     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9726     Store[newax][neway] = element;
9727   }
9728   else if (neway == ay || element == EL_EMC_DRIPPER)
9729   {
9730     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9731
9732     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9733   }
9734   else
9735   {
9736     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9737     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9738     Store[ax][ay] = EL_AMOEBA_DROP;
9739     ContinueMoving(ax, ay);
9740     return;
9741   }
9742
9743   TEST_DrawLevelField(newax, neway);
9744 }
9745
9746 void Life(int ax, int ay)
9747 {
9748   int x1, y1, x2, y2;
9749   int life_time = 40;
9750   int element = Feld[ax][ay];
9751   int graphic = el2img(element);
9752   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9753                          level.biomaze);
9754   boolean changed = FALSE;
9755
9756   if (IS_ANIMATED(graphic))
9757     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9758
9759   if (Stop[ax][ay])
9760     return;
9761
9762   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9763     MovDelay[ax][ay] = life_time;
9764
9765   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9766   {
9767     MovDelay[ax][ay]--;
9768     if (MovDelay[ax][ay])
9769       return;
9770   }
9771
9772   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9773   {
9774     int xx = ax+x1, yy = ay+y1;
9775     int nachbarn = 0;
9776
9777     if (!IN_LEV_FIELD(xx, yy))
9778       continue;
9779
9780     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9781     {
9782       int x = xx+x2, y = yy+y2;
9783
9784       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9785         continue;
9786
9787       if (((Feld[x][y] == element ||
9788             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9789            !Stop[x][y]) ||
9790           (IS_FREE(x, y) && Stop[x][y]))
9791         nachbarn++;
9792     }
9793
9794     if (xx == ax && yy == ay)           /* field in the middle */
9795     {
9796       if (nachbarn < life_parameter[0] ||
9797           nachbarn > life_parameter[1])
9798       {
9799         Feld[xx][yy] = EL_EMPTY;
9800         if (!Stop[xx][yy])
9801           TEST_DrawLevelField(xx, yy);
9802         Stop[xx][yy] = TRUE;
9803         changed = TRUE;
9804       }
9805     }
9806     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9807     {                                   /* free border field */
9808       if (nachbarn >= life_parameter[2] &&
9809           nachbarn <= life_parameter[3])
9810       {
9811         Feld[xx][yy] = element;
9812         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9813         if (!Stop[xx][yy])
9814           TEST_DrawLevelField(xx, yy);
9815         Stop[xx][yy] = TRUE;
9816         changed = TRUE;
9817       }
9818     }
9819   }
9820
9821   if (changed)
9822     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9823                    SND_GAME_OF_LIFE_GROWING);
9824 }
9825
9826 static void InitRobotWheel(int x, int y)
9827 {
9828   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9829 }
9830
9831 static void RunRobotWheel(int x, int y)
9832 {
9833   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9834 }
9835
9836 static void StopRobotWheel(int x, int y)
9837 {
9838   if (ZX == x && ZY == y)
9839   {
9840     ZX = ZY = -1;
9841
9842     game.robot_wheel_active = FALSE;
9843   }
9844 }
9845
9846 static void InitTimegateWheel(int x, int y)
9847 {
9848   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9849 }
9850
9851 static void RunTimegateWheel(int x, int y)
9852 {
9853   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9854 }
9855
9856 static void InitMagicBallDelay(int x, int y)
9857 {
9858 #if 1
9859   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9860 #else
9861   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9862 #endif
9863 }
9864
9865 static void ActivateMagicBall(int bx, int by)
9866 {
9867   int x, y;
9868
9869   if (level.ball_random)
9870   {
9871     int pos_border = RND(8);    /* select one of the eight border elements */
9872     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9873     int xx = pos_content % 3;
9874     int yy = pos_content / 3;
9875
9876     x = bx - 1 + xx;
9877     y = by - 1 + yy;
9878
9879     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9880       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9881   }
9882   else
9883   {
9884     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9885     {
9886       int xx = x - bx + 1;
9887       int yy = y - by + 1;
9888
9889       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9890         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9891     }
9892   }
9893
9894   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9895 }
9896
9897 void CheckExit(int x, int y)
9898 {
9899   if (local_player->gems_still_needed > 0 ||
9900       local_player->sokobanfields_still_needed > 0 ||
9901       local_player->lights_still_needed > 0)
9902   {
9903     int element = Feld[x][y];
9904     int graphic = el2img(element);
9905
9906     if (IS_ANIMATED(graphic))
9907       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9908
9909     return;
9910   }
9911
9912   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9913     return;
9914
9915   Feld[x][y] = EL_EXIT_OPENING;
9916
9917   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9918 }
9919
9920 void CheckExitEM(int x, int y)
9921 {
9922   if (local_player->gems_still_needed > 0 ||
9923       local_player->sokobanfields_still_needed > 0 ||
9924       local_player->lights_still_needed > 0)
9925   {
9926     int element = Feld[x][y];
9927     int graphic = el2img(element);
9928
9929     if (IS_ANIMATED(graphic))
9930       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9931
9932     return;
9933   }
9934
9935   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9936     return;
9937
9938   Feld[x][y] = EL_EM_EXIT_OPENING;
9939
9940   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9941 }
9942
9943 void CheckExitSteel(int x, int y)
9944 {
9945   if (local_player->gems_still_needed > 0 ||
9946       local_player->sokobanfields_still_needed > 0 ||
9947       local_player->lights_still_needed > 0)
9948   {
9949     int element = Feld[x][y];
9950     int graphic = el2img(element);
9951
9952     if (IS_ANIMATED(graphic))
9953       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9954
9955     return;
9956   }
9957
9958   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9959     return;
9960
9961   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9962
9963   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9964 }
9965
9966 void CheckExitSteelEM(int x, int y)
9967 {
9968   if (local_player->gems_still_needed > 0 ||
9969       local_player->sokobanfields_still_needed > 0 ||
9970       local_player->lights_still_needed > 0)
9971   {
9972     int element = Feld[x][y];
9973     int graphic = el2img(element);
9974
9975     if (IS_ANIMATED(graphic))
9976       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9977
9978     return;
9979   }
9980
9981   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9982     return;
9983
9984   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9985
9986   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9987 }
9988
9989 void CheckExitSP(int x, int y)
9990 {
9991   if (local_player->gems_still_needed > 0)
9992   {
9993     int element = Feld[x][y];
9994     int graphic = el2img(element);
9995
9996     if (IS_ANIMATED(graphic))
9997       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9998
9999     return;
10000   }
10001
10002   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10003     return;
10004
10005   Feld[x][y] = EL_SP_EXIT_OPENING;
10006
10007   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10008 }
10009
10010 static void CloseAllOpenTimegates()
10011 {
10012   int x, y;
10013
10014   SCAN_PLAYFIELD(x, y)
10015   {
10016     int element = Feld[x][y];
10017
10018     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10019     {
10020       Feld[x][y] = EL_TIMEGATE_CLOSING;
10021
10022       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10023     }
10024   }
10025 }
10026
10027 void DrawTwinkleOnField(int x, int y)
10028 {
10029   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10030     return;
10031
10032   if (Feld[x][y] == EL_BD_DIAMOND)
10033     return;
10034
10035   if (MovDelay[x][y] == 0)      /* next animation frame */
10036     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10037
10038   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10039   {
10040     MovDelay[x][y]--;
10041
10042     DrawLevelElementAnimation(x, y, Feld[x][y]);
10043
10044     if (MovDelay[x][y] != 0)
10045     {
10046       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10047                                            10 - MovDelay[x][y]);
10048
10049       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10050     }
10051   }
10052 }
10053
10054 void MauerWaechst(int x, int y)
10055 {
10056   int delay = 6;
10057
10058   if (!MovDelay[x][y])          /* next animation frame */
10059     MovDelay[x][y] = 3 * delay;
10060
10061   if (MovDelay[x][y])           /* wait some time before next frame */
10062   {
10063     MovDelay[x][y]--;
10064
10065     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10066     {
10067       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10068       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10069
10070       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10071     }
10072
10073     if (!MovDelay[x][y])
10074     {
10075       if (MovDir[x][y] == MV_LEFT)
10076       {
10077         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10078           TEST_DrawLevelField(x - 1, y);
10079       }
10080       else if (MovDir[x][y] == MV_RIGHT)
10081       {
10082         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10083           TEST_DrawLevelField(x + 1, y);
10084       }
10085       else if (MovDir[x][y] == MV_UP)
10086       {
10087         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10088           TEST_DrawLevelField(x, y - 1);
10089       }
10090       else
10091       {
10092         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10093           TEST_DrawLevelField(x, y + 1);
10094       }
10095
10096       Feld[x][y] = Store[x][y];
10097       Store[x][y] = 0;
10098       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10099       TEST_DrawLevelField(x, y);
10100     }
10101   }
10102 }
10103
10104 void MauerAbleger(int ax, int ay)
10105 {
10106   int element = Feld[ax][ay];
10107   int graphic = el2img(element);
10108   boolean oben_frei = FALSE, unten_frei = FALSE;
10109   boolean links_frei = FALSE, rechts_frei = FALSE;
10110   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10111   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10112   boolean new_wall = FALSE;
10113
10114   if (IS_ANIMATED(graphic))
10115     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10116
10117   if (!MovDelay[ax][ay])        /* start building new wall */
10118     MovDelay[ax][ay] = 6;
10119
10120   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10121   {
10122     MovDelay[ax][ay]--;
10123     if (MovDelay[ax][ay])
10124       return;
10125   }
10126
10127   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10128     oben_frei = TRUE;
10129   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10130     unten_frei = TRUE;
10131   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10132     links_frei = TRUE;
10133   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10134     rechts_frei = TRUE;
10135
10136   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10137       element == EL_EXPANDABLE_WALL_ANY)
10138   {
10139     if (oben_frei)
10140     {
10141       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10142       Store[ax][ay-1] = element;
10143       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10144       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10145         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10146                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10147       new_wall = TRUE;
10148     }
10149     if (unten_frei)
10150     {
10151       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10152       Store[ax][ay+1] = element;
10153       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10154       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10155         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10156                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10157       new_wall = TRUE;
10158     }
10159   }
10160
10161   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10162       element == EL_EXPANDABLE_WALL_ANY ||
10163       element == EL_EXPANDABLE_WALL ||
10164       element == EL_BD_EXPANDABLE_WALL)
10165   {
10166     if (links_frei)
10167     {
10168       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10169       Store[ax-1][ay] = element;
10170       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10171       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10172         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10173                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10174       new_wall = TRUE;
10175     }
10176
10177     if (rechts_frei)
10178     {
10179       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10180       Store[ax+1][ay] = element;
10181       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10182       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10183         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10184                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10185       new_wall = TRUE;
10186     }
10187   }
10188
10189   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10190     TEST_DrawLevelField(ax, ay);
10191
10192   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10193     oben_massiv = TRUE;
10194   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10195     unten_massiv = TRUE;
10196   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10197     links_massiv = TRUE;
10198   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10199     rechts_massiv = TRUE;
10200
10201   if (((oben_massiv && unten_massiv) ||
10202        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10203        element == EL_EXPANDABLE_WALL) &&
10204       ((links_massiv && rechts_massiv) ||
10205        element == EL_EXPANDABLE_WALL_VERTICAL))
10206     Feld[ax][ay] = EL_WALL;
10207
10208   if (new_wall)
10209     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10210 }
10211
10212 void MauerAblegerStahl(int ax, int ay)
10213 {
10214   int element = Feld[ax][ay];
10215   int graphic = el2img(element);
10216   boolean oben_frei = FALSE, unten_frei = FALSE;
10217   boolean links_frei = FALSE, rechts_frei = FALSE;
10218   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10219   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10220   boolean new_wall = FALSE;
10221
10222   if (IS_ANIMATED(graphic))
10223     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10224
10225   if (!MovDelay[ax][ay])        /* start building new wall */
10226     MovDelay[ax][ay] = 6;
10227
10228   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10229   {
10230     MovDelay[ax][ay]--;
10231     if (MovDelay[ax][ay])
10232       return;
10233   }
10234
10235   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10236     oben_frei = TRUE;
10237   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10238     unten_frei = TRUE;
10239   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10240     links_frei = TRUE;
10241   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10242     rechts_frei = TRUE;
10243
10244   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10245       element == EL_EXPANDABLE_STEELWALL_ANY)
10246   {
10247     if (oben_frei)
10248     {
10249       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10250       Store[ax][ay-1] = element;
10251       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10252       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10253         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10254                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10255       new_wall = TRUE;
10256     }
10257     if (unten_frei)
10258     {
10259       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10260       Store[ax][ay+1] = element;
10261       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10262       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10263         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10264                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10265       new_wall = TRUE;
10266     }
10267   }
10268
10269   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10270       element == EL_EXPANDABLE_STEELWALL_ANY)
10271   {
10272     if (links_frei)
10273     {
10274       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10275       Store[ax-1][ay] = element;
10276       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10277       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10278         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10279                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10280       new_wall = TRUE;
10281     }
10282
10283     if (rechts_frei)
10284     {
10285       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10286       Store[ax+1][ay] = element;
10287       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10288       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10289         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10290                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10291       new_wall = TRUE;
10292     }
10293   }
10294
10295   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10296     oben_massiv = TRUE;
10297   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10298     unten_massiv = TRUE;
10299   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10300     links_massiv = TRUE;
10301   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10302     rechts_massiv = TRUE;
10303
10304   if (((oben_massiv && unten_massiv) ||
10305        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10306       ((links_massiv && rechts_massiv) ||
10307        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10308     Feld[ax][ay] = EL_STEELWALL;
10309
10310   if (new_wall)
10311     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10312 }
10313
10314 void CheckForDragon(int x, int y)
10315 {
10316   int i, j;
10317   boolean dragon_found = FALSE;
10318   static int xy[4][2] =
10319   {
10320     { 0, -1 },
10321     { -1, 0 },
10322     { +1, 0 },
10323     { 0, +1 }
10324   };
10325
10326   for (i = 0; i < NUM_DIRECTIONS; i++)
10327   {
10328     for (j = 0; j < 4; j++)
10329     {
10330       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10331
10332       if (IN_LEV_FIELD(xx, yy) &&
10333           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10334       {
10335         if (Feld[xx][yy] == EL_DRAGON)
10336           dragon_found = TRUE;
10337       }
10338       else
10339         break;
10340     }
10341   }
10342
10343   if (!dragon_found)
10344   {
10345     for (i = 0; i < NUM_DIRECTIONS; i++)
10346     {
10347       for (j = 0; j < 3; j++)
10348       {
10349         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10350   
10351         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10352         {
10353           Feld[xx][yy] = EL_EMPTY;
10354           TEST_DrawLevelField(xx, yy);
10355         }
10356         else
10357           break;
10358       }
10359     }
10360   }
10361 }
10362
10363 static void InitBuggyBase(int x, int y)
10364 {
10365   int element = Feld[x][y];
10366   int activating_delay = FRAMES_PER_SECOND / 4;
10367
10368   ChangeDelay[x][y] =
10369     (element == EL_SP_BUGGY_BASE ?
10370      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10371      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10372      activating_delay :
10373      element == EL_SP_BUGGY_BASE_ACTIVE ?
10374      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10375 }
10376
10377 static void WarnBuggyBase(int x, int y)
10378 {
10379   int i;
10380   static int xy[4][2] =
10381   {
10382     { 0, -1 },
10383     { -1, 0 },
10384     { +1, 0 },
10385     { 0, +1 }
10386   };
10387
10388   for (i = 0; i < NUM_DIRECTIONS; i++)
10389   {
10390     int xx = x + xy[i][0];
10391     int yy = y + xy[i][1];
10392
10393     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10394     {
10395       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10396
10397       break;
10398     }
10399   }
10400 }
10401
10402 static void InitTrap(int x, int y)
10403 {
10404   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10405 }
10406
10407 static void ActivateTrap(int x, int y)
10408 {
10409   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10410 }
10411
10412 static void ChangeActiveTrap(int x, int y)
10413 {
10414   int graphic = IMG_TRAP_ACTIVE;
10415
10416   /* if new animation frame was drawn, correct crumbled sand border */
10417   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10418     TEST_DrawLevelFieldCrumbled(x, y);
10419 }
10420
10421 static int getSpecialActionElement(int element, int number, int base_element)
10422 {
10423   return (element != EL_EMPTY ? element :
10424           number != -1 ? base_element + number - 1 :
10425           EL_EMPTY);
10426 }
10427
10428 static int getModifiedActionNumber(int value_old, int operator, int operand,
10429                                    int value_min, int value_max)
10430 {
10431   int value_new = (operator == CA_MODE_SET      ? operand :
10432                    operator == CA_MODE_ADD      ? value_old + operand :
10433                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10434                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10435                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10436                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10437                    value_old);
10438
10439   return (value_new < value_min ? value_min :
10440           value_new > value_max ? value_max :
10441           value_new);
10442 }
10443
10444 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10445 {
10446   struct ElementInfo *ei = &element_info[element];
10447   struct ElementChangeInfo *change = &ei->change_page[page];
10448   int target_element = change->target_element;
10449   int action_type = change->action_type;
10450   int action_mode = change->action_mode;
10451   int action_arg = change->action_arg;
10452   int action_element = change->action_element;
10453   int i;
10454
10455   if (!change->has_action)
10456     return;
10457
10458   /* ---------- determine action paramater values -------------------------- */
10459
10460   int level_time_value =
10461     (level.time > 0 ? TimeLeft :
10462      TimePlayed);
10463
10464   int action_arg_element_raw =
10465     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10466      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10467      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10468      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10469      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10470      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10471      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10472      EL_EMPTY);
10473   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10474
10475 #if 0
10476   if (action_arg_element_raw == EL_GROUP_START)
10477     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10478 #endif
10479
10480   int action_arg_direction =
10481     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10482      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10483      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10484      change->actual_trigger_side :
10485      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10486      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10487      MV_NONE);
10488
10489   int action_arg_number_min =
10490     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10491      CA_ARG_MIN);
10492
10493   int action_arg_number_max =
10494     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10495      action_type == CA_SET_LEVEL_GEMS ? 999 :
10496      action_type == CA_SET_LEVEL_TIME ? 9999 :
10497      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10498      action_type == CA_SET_CE_VALUE ? 9999 :
10499      action_type == CA_SET_CE_SCORE ? 9999 :
10500      CA_ARG_MAX);
10501
10502   int action_arg_number_reset =
10503     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10504      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10505      action_type == CA_SET_LEVEL_TIME ? level.time :
10506      action_type == CA_SET_LEVEL_SCORE ? 0 :
10507 #if USE_NEW_CUSTOM_VALUE
10508      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10509 #else
10510      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10511 #endif
10512      action_type == CA_SET_CE_SCORE ? 0 :
10513      0);
10514
10515   int action_arg_number =
10516     (action_arg <= CA_ARG_MAX ? action_arg :
10517      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10518      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10519      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10520      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10521      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10522      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10523 #if USE_NEW_CUSTOM_VALUE
10524      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10525 #else
10526      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10527 #endif
10528      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10529      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10530      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10531      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10532      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10533      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10534      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10535      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10536      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10537      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10538      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10539      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10540      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10541      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10542      -1);
10543
10544   int action_arg_number_old =
10545     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10546      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10547      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10548      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10549      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10550      0);
10551
10552   int action_arg_number_new =
10553     getModifiedActionNumber(action_arg_number_old,
10554                             action_mode, action_arg_number,
10555                             action_arg_number_min, action_arg_number_max);
10556
10557 #if 1
10558   int trigger_player_bits =
10559     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10560      change->actual_trigger_player_bits : change->trigger_player);
10561 #else
10562   int trigger_player_bits =
10563     (change->actual_trigger_player >= EL_PLAYER_1 &&
10564      change->actual_trigger_player <= EL_PLAYER_4 ?
10565      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10566      PLAYER_BITS_ANY);
10567 #endif
10568
10569   int action_arg_player_bits =
10570     (action_arg >= CA_ARG_PLAYER_1 &&
10571      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10572      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10573      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10574      PLAYER_BITS_ANY);
10575
10576   /* ---------- execute action  -------------------------------------------- */
10577
10578   switch (action_type)
10579   {
10580     case CA_NO_ACTION:
10581     {
10582       return;
10583     }
10584
10585     /* ---------- level actions  ------------------------------------------- */
10586
10587     case CA_RESTART_LEVEL:
10588     {
10589       game.restart_level = TRUE;
10590
10591       break;
10592     }
10593
10594     case CA_SHOW_ENVELOPE:
10595     {
10596       int element = getSpecialActionElement(action_arg_element,
10597                                             action_arg_number, EL_ENVELOPE_1);
10598
10599       if (IS_ENVELOPE(element))
10600         local_player->show_envelope = element;
10601
10602       break;
10603     }
10604
10605     case CA_SET_LEVEL_TIME:
10606     {
10607       if (level.time > 0)       /* only modify limited time value */
10608       {
10609         TimeLeft = action_arg_number_new;
10610
10611 #if 1
10612         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10613
10614         DisplayGameControlValues();
10615 #else
10616         DrawGameValue_Time(TimeLeft);
10617 #endif
10618
10619         if (!TimeLeft && setup.time_limit)
10620           for (i = 0; i < MAX_PLAYERS; i++)
10621             KillPlayer(&stored_player[i]);
10622       }
10623
10624       break;
10625     }
10626
10627     case CA_SET_LEVEL_SCORE:
10628     {
10629       local_player->score = action_arg_number_new;
10630
10631 #if 1
10632       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10633
10634       DisplayGameControlValues();
10635 #else
10636       DrawGameValue_Score(local_player->score);
10637 #endif
10638
10639       break;
10640     }
10641
10642     case CA_SET_LEVEL_GEMS:
10643     {
10644       local_player->gems_still_needed = action_arg_number_new;
10645
10646 #if 1
10647       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10648
10649       DisplayGameControlValues();
10650 #else
10651       DrawGameValue_Emeralds(local_player->gems_still_needed);
10652 #endif
10653
10654       break;
10655     }
10656
10657 #if !USE_PLAYER_GRAVITY
10658     case CA_SET_LEVEL_GRAVITY:
10659     {
10660       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10661                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10662                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10663                       game.gravity);
10664       break;
10665     }
10666 #endif
10667
10668     case CA_SET_LEVEL_WIND:
10669     {
10670       game.wind_direction = action_arg_direction;
10671
10672       break;
10673     }
10674
10675     case CA_SET_LEVEL_RANDOM_SEED:
10676     {
10677 #if 1
10678       /* ensure that setting a new random seed while playing is predictable */
10679       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10680 #else
10681       InitRND(action_arg_number_new);
10682 #endif
10683
10684 #if 0
10685       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10686 #endif
10687
10688 #if 0
10689       {
10690         int i;
10691
10692         printf("::: ");
10693         for (i = 0; i < 9; i++)
10694           printf("%d, ", RND(2));
10695         printf("\n");
10696       }
10697 #endif
10698
10699       break;
10700     }
10701
10702     /* ---------- player actions  ------------------------------------------ */
10703
10704     case CA_MOVE_PLAYER:
10705     {
10706       /* automatically move to the next field in specified direction */
10707       for (i = 0; i < MAX_PLAYERS; i++)
10708         if (trigger_player_bits & (1 << i))
10709           stored_player[i].programmed_action = action_arg_direction;
10710
10711       break;
10712     }
10713
10714     case CA_EXIT_PLAYER:
10715     {
10716       for (i = 0; i < MAX_PLAYERS; i++)
10717         if (action_arg_player_bits & (1 << i))
10718           PlayerWins(&stored_player[i]);
10719
10720       break;
10721     }
10722
10723     case CA_KILL_PLAYER:
10724     {
10725       for (i = 0; i < MAX_PLAYERS; i++)
10726         if (action_arg_player_bits & (1 << i))
10727           KillPlayer(&stored_player[i]);
10728
10729       break;
10730     }
10731
10732     case CA_SET_PLAYER_KEYS:
10733     {
10734       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10735       int element = getSpecialActionElement(action_arg_element,
10736                                             action_arg_number, EL_KEY_1);
10737
10738       if (IS_KEY(element))
10739       {
10740         for (i = 0; i < MAX_PLAYERS; i++)
10741         {
10742           if (trigger_player_bits & (1 << i))
10743           {
10744             stored_player[i].key[KEY_NR(element)] = key_state;
10745
10746             DrawGameDoorValues();
10747           }
10748         }
10749       }
10750
10751       break;
10752     }
10753
10754     case CA_SET_PLAYER_SPEED:
10755     {
10756 #if 0
10757       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10758 #endif
10759
10760       for (i = 0; i < MAX_PLAYERS; i++)
10761       {
10762         if (trigger_player_bits & (1 << i))
10763         {
10764           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10765
10766           if (action_arg == CA_ARG_SPEED_FASTER &&
10767               stored_player[i].cannot_move)
10768           {
10769             action_arg_number = STEPSIZE_VERY_SLOW;
10770           }
10771           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10772                    action_arg == CA_ARG_SPEED_FASTER)
10773           {
10774             action_arg_number = 2;
10775             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10776                            CA_MODE_MULTIPLY);
10777           }
10778           else if (action_arg == CA_ARG_NUMBER_RESET)
10779           {
10780             action_arg_number = level.initial_player_stepsize[i];
10781           }
10782
10783           move_stepsize =
10784             getModifiedActionNumber(move_stepsize,
10785                                     action_mode,
10786                                     action_arg_number,
10787                                     action_arg_number_min,
10788                                     action_arg_number_max);
10789
10790           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10791         }
10792       }
10793
10794       break;
10795     }
10796
10797     case CA_SET_PLAYER_SHIELD:
10798     {
10799       for (i = 0; i < MAX_PLAYERS; i++)
10800       {
10801         if (trigger_player_bits & (1 << i))
10802         {
10803           if (action_arg == CA_ARG_SHIELD_OFF)
10804           {
10805             stored_player[i].shield_normal_time_left = 0;
10806             stored_player[i].shield_deadly_time_left = 0;
10807           }
10808           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10809           {
10810             stored_player[i].shield_normal_time_left = 999999;
10811           }
10812           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10813           {
10814             stored_player[i].shield_normal_time_left = 999999;
10815             stored_player[i].shield_deadly_time_left = 999999;
10816           }
10817         }
10818       }
10819
10820       break;
10821     }
10822
10823 #if USE_PLAYER_GRAVITY
10824     case CA_SET_PLAYER_GRAVITY:
10825     {
10826       for (i = 0; i < MAX_PLAYERS; i++)
10827       {
10828         if (trigger_player_bits & (1 << i))
10829         {
10830           stored_player[i].gravity =
10831             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10832              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10833              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10834              stored_player[i].gravity);
10835         }
10836       }
10837
10838       break;
10839     }
10840 #endif
10841
10842     case CA_SET_PLAYER_ARTWORK:
10843     {
10844       for (i = 0; i < MAX_PLAYERS; i++)
10845       {
10846         if (trigger_player_bits & (1 << i))
10847         {
10848           int artwork_element = action_arg_element;
10849
10850           if (action_arg == CA_ARG_ELEMENT_RESET)
10851             artwork_element =
10852               (level.use_artwork_element[i] ? level.artwork_element[i] :
10853                stored_player[i].element_nr);
10854
10855 #if USE_GFX_RESET_PLAYER_ARTWORK
10856           if (stored_player[i].artwork_element != artwork_element)
10857             stored_player[i].Frame = 0;
10858 #endif
10859
10860           stored_player[i].artwork_element = artwork_element;
10861
10862           SetPlayerWaiting(&stored_player[i], FALSE);
10863
10864           /* set number of special actions for bored and sleeping animation */
10865           stored_player[i].num_special_action_bored =
10866             get_num_special_action(artwork_element,
10867                                    ACTION_BORING_1, ACTION_BORING_LAST);
10868           stored_player[i].num_special_action_sleeping =
10869             get_num_special_action(artwork_element,
10870                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10871         }
10872       }
10873
10874       break;
10875     }
10876
10877     case CA_SET_PLAYER_INVENTORY:
10878     {
10879       for (i = 0; i < MAX_PLAYERS; i++)
10880       {
10881         struct PlayerInfo *player = &stored_player[i];
10882         int j, k;
10883
10884         if (trigger_player_bits & (1 << i))
10885         {
10886           int inventory_element = action_arg_element;
10887
10888           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10889               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10890               action_arg == CA_ARG_ELEMENT_ACTION)
10891           {
10892             int element = inventory_element;
10893             int collect_count = element_info[element].collect_count_initial;
10894
10895             if (!IS_CUSTOM_ELEMENT(element))
10896               collect_count = 1;
10897
10898             if (collect_count == 0)
10899               player->inventory_infinite_element = element;
10900             else
10901               for (k = 0; k < collect_count; k++)
10902                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10903                   player->inventory_element[player->inventory_size++] =
10904                     element;
10905           }
10906           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10907                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10908                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10909           {
10910             if (player->inventory_infinite_element != EL_UNDEFINED &&
10911                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10912                                      action_arg_element_raw))
10913               player->inventory_infinite_element = EL_UNDEFINED;
10914
10915             for (k = 0, j = 0; j < player->inventory_size; j++)
10916             {
10917               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10918                                         action_arg_element_raw))
10919                 player->inventory_element[k++] = player->inventory_element[j];
10920             }
10921
10922             player->inventory_size = k;
10923           }
10924           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10925           {
10926             if (player->inventory_size > 0)
10927             {
10928               for (j = 0; j < player->inventory_size - 1; j++)
10929                 player->inventory_element[j] = player->inventory_element[j + 1];
10930
10931               player->inventory_size--;
10932             }
10933           }
10934           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10935           {
10936             if (player->inventory_size > 0)
10937               player->inventory_size--;
10938           }
10939           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10940           {
10941             player->inventory_infinite_element = EL_UNDEFINED;
10942             player->inventory_size = 0;
10943           }
10944           else if (action_arg == CA_ARG_INVENTORY_RESET)
10945           {
10946             player->inventory_infinite_element = EL_UNDEFINED;
10947             player->inventory_size = 0;
10948
10949             if (level.use_initial_inventory[i])
10950             {
10951               for (j = 0; j < level.initial_inventory_size[i]; j++)
10952               {
10953                 int element = level.initial_inventory_content[i][j];
10954                 int collect_count = element_info[element].collect_count_initial;
10955
10956                 if (!IS_CUSTOM_ELEMENT(element))
10957                   collect_count = 1;
10958
10959                 if (collect_count == 0)
10960                   player->inventory_infinite_element = element;
10961                 else
10962                   for (k = 0; k < collect_count; k++)
10963                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10964                       player->inventory_element[player->inventory_size++] =
10965                         element;
10966               }
10967             }
10968           }
10969         }
10970       }
10971
10972       break;
10973     }
10974
10975     /* ---------- CE actions  ---------------------------------------------- */
10976
10977     case CA_SET_CE_VALUE:
10978     {
10979 #if USE_NEW_CUSTOM_VALUE
10980       int last_ce_value = CustomValue[x][y];
10981
10982       CustomValue[x][y] = action_arg_number_new;
10983
10984       if (CustomValue[x][y] != last_ce_value)
10985       {
10986         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10987         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10988
10989         if (CustomValue[x][y] == 0)
10990         {
10991           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10992           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10993         }
10994       }
10995 #endif
10996
10997       break;
10998     }
10999
11000     case CA_SET_CE_SCORE:
11001     {
11002 #if USE_NEW_CUSTOM_VALUE
11003       int last_ce_score = ei->collect_score;
11004
11005       ei->collect_score = action_arg_number_new;
11006
11007       if (ei->collect_score != last_ce_score)
11008       {
11009         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11010         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11011
11012         if (ei->collect_score == 0)
11013         {
11014           int xx, yy;
11015
11016           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11017           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11018
11019           /*
11020             This is a very special case that seems to be a mixture between
11021             CheckElementChange() and CheckTriggeredElementChange(): while
11022             the first one only affects single elements that are triggered
11023             directly, the second one affects multiple elements in the playfield
11024             that are triggered indirectly by another element. This is a third
11025             case: Changing the CE score always affects multiple identical CEs,
11026             so every affected CE must be checked, not only the single CE for
11027             which the CE score was changed in the first place (as every instance
11028             of that CE shares the same CE score, and therefore also can change)!
11029           */
11030           SCAN_PLAYFIELD(xx, yy)
11031           {
11032             if (Feld[xx][yy] == element)
11033               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11034                                  CE_SCORE_GETS_ZERO);
11035           }
11036         }
11037       }
11038 #endif
11039
11040       break;
11041     }
11042
11043     case CA_SET_CE_ARTWORK:
11044     {
11045       int artwork_element = action_arg_element;
11046       boolean reset_frame = FALSE;
11047       int xx, yy;
11048
11049       if (action_arg == CA_ARG_ELEMENT_RESET)
11050         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11051                            element);
11052
11053       if (ei->gfx_element != artwork_element)
11054         reset_frame = TRUE;
11055
11056       ei->gfx_element = artwork_element;
11057
11058       SCAN_PLAYFIELD(xx, yy)
11059       {
11060         if (Feld[xx][yy] == element)
11061         {
11062           if (reset_frame)
11063           {
11064             ResetGfxAnimation(xx, yy);
11065             ResetRandomAnimationValue(xx, yy);
11066           }
11067
11068           TEST_DrawLevelField(xx, yy);
11069         }
11070       }
11071
11072       break;
11073     }
11074
11075     /* ---------- engine actions  ------------------------------------------ */
11076
11077     case CA_SET_ENGINE_SCAN_MODE:
11078     {
11079       InitPlayfieldScanMode(action_arg);
11080
11081       break;
11082     }
11083
11084     default:
11085       break;
11086   }
11087 }
11088
11089 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11090 {
11091   int old_element = Feld[x][y];
11092   int new_element = GetElementFromGroupElement(element);
11093   int previous_move_direction = MovDir[x][y];
11094 #if USE_NEW_CUSTOM_VALUE
11095   int last_ce_value = CustomValue[x][y];
11096 #endif
11097   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11098   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11099   boolean add_player_onto_element = (new_element_is_player &&
11100 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11101                                      /* this breaks SnakeBite when a snake is
11102                                         halfway through a door that closes */
11103                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11104                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11105 #endif
11106                                      IS_WALKABLE(old_element));
11107
11108 #if 0
11109   /* check if element under the player changes from accessible to unaccessible
11110      (needed for special case of dropping element which then changes) */
11111   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11112       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11113   {
11114     Bang(x, y);
11115
11116     return;
11117   }
11118 #endif
11119
11120   if (!add_player_onto_element)
11121   {
11122     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11123       RemoveMovingField(x, y);
11124     else
11125       RemoveField(x, y);
11126
11127     Feld[x][y] = new_element;
11128
11129 #if !USE_GFX_RESET_GFX_ANIMATION
11130     ResetGfxAnimation(x, y);
11131     ResetRandomAnimationValue(x, y);
11132 #endif
11133
11134     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11135       MovDir[x][y] = previous_move_direction;
11136
11137 #if USE_NEW_CUSTOM_VALUE
11138     if (element_info[new_element].use_last_ce_value)
11139       CustomValue[x][y] = last_ce_value;
11140 #endif
11141
11142     InitField_WithBug1(x, y, FALSE);
11143
11144     new_element = Feld[x][y];   /* element may have changed */
11145
11146 #if USE_GFX_RESET_GFX_ANIMATION
11147     ResetGfxAnimation(x, y);
11148     ResetRandomAnimationValue(x, y);
11149 #endif
11150
11151     TEST_DrawLevelField(x, y);
11152
11153     if (GFX_CRUMBLED(new_element))
11154       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11155   }
11156
11157 #if 1
11158   /* check if element under the player changes from accessible to unaccessible
11159      (needed for special case of dropping element which then changes) */
11160   /* (must be checked after creating new element for walkable group elements) */
11161 #if USE_FIX_KILLED_BY_NON_WALKABLE
11162   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11163       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11164   {
11165     Bang(x, y);
11166
11167     return;
11168   }
11169 #else
11170   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11171       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11172   {
11173     Bang(x, y);
11174
11175     return;
11176   }
11177 #endif
11178 #endif
11179
11180   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11181   if (new_element_is_player)
11182     RelocatePlayer(x, y, new_element);
11183
11184   if (is_change)
11185     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11186
11187   TestIfBadThingTouchesPlayer(x, y);
11188   TestIfPlayerTouchesCustomElement(x, y);
11189   TestIfElementTouchesCustomElement(x, y);
11190 }
11191
11192 static void CreateField(int x, int y, int element)
11193 {
11194   CreateFieldExt(x, y, element, FALSE);
11195 }
11196
11197 static void CreateElementFromChange(int x, int y, int element)
11198 {
11199   element = GET_VALID_RUNTIME_ELEMENT(element);
11200
11201 #if USE_STOP_CHANGED_ELEMENTS
11202   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11203   {
11204     int old_element = Feld[x][y];
11205
11206     /* prevent changed element from moving in same engine frame
11207        unless both old and new element can either fall or move */
11208     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11209         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11210       Stop[x][y] = TRUE;
11211   }
11212 #endif
11213
11214   CreateFieldExt(x, y, element, TRUE);
11215 }
11216
11217 static boolean ChangeElement(int x, int y, int element, int page)
11218 {
11219   struct ElementInfo *ei = &element_info[element];
11220   struct ElementChangeInfo *change = &ei->change_page[page];
11221   int ce_value = CustomValue[x][y];
11222   int ce_score = ei->collect_score;
11223   int target_element;
11224   int old_element = Feld[x][y];
11225
11226   /* always use default change event to prevent running into a loop */
11227   if (ChangeEvent[x][y] == -1)
11228     ChangeEvent[x][y] = CE_DELAY;
11229
11230   if (ChangeEvent[x][y] == CE_DELAY)
11231   {
11232     /* reset actual trigger element, trigger player and action element */
11233     change->actual_trigger_element = EL_EMPTY;
11234     change->actual_trigger_player = EL_EMPTY;
11235     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11236     change->actual_trigger_side = CH_SIDE_NONE;
11237     change->actual_trigger_ce_value = 0;
11238     change->actual_trigger_ce_score = 0;
11239   }
11240
11241   /* do not change elements more than a specified maximum number of changes */
11242   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11243     return FALSE;
11244
11245   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11246
11247   if (change->explode)
11248   {
11249     Bang(x, y);
11250
11251     return TRUE;
11252   }
11253
11254   if (change->use_target_content)
11255   {
11256     boolean complete_replace = TRUE;
11257     boolean can_replace[3][3];
11258     int xx, yy;
11259
11260     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11261     {
11262       boolean is_empty;
11263       boolean is_walkable;
11264       boolean is_diggable;
11265       boolean is_collectible;
11266       boolean is_removable;
11267       boolean is_destructible;
11268       int ex = x + xx - 1;
11269       int ey = y + yy - 1;
11270       int content_element = change->target_content.e[xx][yy];
11271       int e;
11272
11273       can_replace[xx][yy] = TRUE;
11274
11275       if (ex == x && ey == y)   /* do not check changing element itself */
11276         continue;
11277
11278       if (content_element == EL_EMPTY_SPACE)
11279       {
11280         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11281
11282         continue;
11283       }
11284
11285       if (!IN_LEV_FIELD(ex, ey))
11286       {
11287         can_replace[xx][yy] = FALSE;
11288         complete_replace = FALSE;
11289
11290         continue;
11291       }
11292
11293       e = Feld[ex][ey];
11294
11295       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11296         e = MovingOrBlocked2Element(ex, ey);
11297
11298       is_empty = (IS_FREE(ex, ey) ||
11299                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11300
11301       is_walkable     = (is_empty || IS_WALKABLE(e));
11302       is_diggable     = (is_empty || IS_DIGGABLE(e));
11303       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11304       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11305       is_removable    = (is_diggable || is_collectible);
11306
11307       can_replace[xx][yy] =
11308         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11309           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11310           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11311           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11312           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11313           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11314          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11315
11316       if (!can_replace[xx][yy])
11317         complete_replace = FALSE;
11318     }
11319
11320     if (!change->only_if_complete || complete_replace)
11321     {
11322       boolean something_has_changed = FALSE;
11323
11324       if (change->only_if_complete && change->use_random_replace &&
11325           RND(100) < change->random_percentage)
11326         return FALSE;
11327
11328       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11329       {
11330         int ex = x + xx - 1;
11331         int ey = y + yy - 1;
11332         int content_element;
11333
11334         if (can_replace[xx][yy] && (!change->use_random_replace ||
11335                                     RND(100) < change->random_percentage))
11336         {
11337           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11338             RemoveMovingField(ex, ey);
11339
11340           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11341
11342           content_element = change->target_content.e[xx][yy];
11343           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11344                                               ce_value, ce_score);
11345
11346           CreateElementFromChange(ex, ey, target_element);
11347
11348           something_has_changed = TRUE;
11349
11350           /* for symmetry reasons, freeze newly created border elements */
11351           if (ex != x || ey != y)
11352             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11353         }
11354       }
11355
11356       if (something_has_changed)
11357       {
11358         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11359         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11360       }
11361     }
11362   }
11363   else
11364   {
11365     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11366                                         ce_value, ce_score);
11367
11368     if (element == EL_DIAGONAL_GROWING ||
11369         element == EL_DIAGONAL_SHRINKING)
11370     {
11371       target_element = Store[x][y];
11372
11373       Store[x][y] = EL_EMPTY;
11374     }
11375
11376     CreateElementFromChange(x, y, target_element);
11377
11378     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11379     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11380   }
11381
11382   /* this uses direct change before indirect change */
11383   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11384
11385   return TRUE;
11386 }
11387
11388 #if USE_NEW_DELAYED_ACTION
11389
11390 static void HandleElementChange(int x, int y, int page)
11391 {
11392   int element = MovingOrBlocked2Element(x, y);
11393   struct ElementInfo *ei = &element_info[element];
11394   struct ElementChangeInfo *change = &ei->change_page[page];
11395   boolean handle_action_before_change = FALSE;
11396
11397 #ifdef DEBUG
11398   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11399       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11400   {
11401     printf("\n\n");
11402     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11403            x, y, element, element_info[element].token_name);
11404     printf("HandleElementChange(): This should never happen!\n");
11405     printf("\n\n");
11406   }
11407 #endif
11408
11409   /* this can happen with classic bombs on walkable, changing elements */
11410   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11411   {
11412 #if 0
11413     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11414       ChangeDelay[x][y] = 0;
11415 #endif
11416
11417     return;
11418   }
11419
11420   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11421   {
11422     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11423
11424     if (change->can_change)
11425     {
11426 #if 1
11427       /* !!! not clear why graphic animation should be reset at all here !!! */
11428       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11429 #if USE_GFX_RESET_WHEN_NOT_MOVING
11430       /* when a custom element is about to change (for example by change delay),
11431          do not reset graphic animation when the custom element is moving */
11432       if (!IS_MOVING(x, y))
11433 #endif
11434       {
11435         ResetGfxAnimation(x, y);
11436         ResetRandomAnimationValue(x, y);
11437       }
11438 #endif
11439
11440       if (change->pre_change_function)
11441         change->pre_change_function(x, y);
11442     }
11443   }
11444
11445   ChangeDelay[x][y]--;
11446
11447   if (ChangeDelay[x][y] != 0)           /* continue element change */
11448   {
11449     if (change->can_change)
11450     {
11451       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11452
11453       if (IS_ANIMATED(graphic))
11454         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11455
11456       if (change->change_function)
11457         change->change_function(x, y);
11458     }
11459   }
11460   else                                  /* finish element change */
11461   {
11462     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11463     {
11464       page = ChangePage[x][y];
11465       ChangePage[x][y] = -1;
11466
11467       change = &ei->change_page[page];
11468     }
11469
11470     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11471     {
11472       ChangeDelay[x][y] = 1;            /* try change after next move step */
11473       ChangePage[x][y] = page;          /* remember page to use for change */
11474
11475       return;
11476     }
11477
11478 #if 1
11479     /* special case: set new level random seed before changing element */
11480     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11481       handle_action_before_change = TRUE;
11482
11483     if (change->has_action && handle_action_before_change)
11484       ExecuteCustomElementAction(x, y, element, page);
11485 #endif
11486
11487     if (change->can_change)
11488     {
11489       if (ChangeElement(x, y, element, page))
11490       {
11491         if (change->post_change_function)
11492           change->post_change_function(x, y);
11493       }
11494     }
11495
11496     if (change->has_action && !handle_action_before_change)
11497       ExecuteCustomElementAction(x, y, element, page);
11498   }
11499 }
11500
11501 #else
11502
11503 static void HandleElementChange(int x, int y, int page)
11504 {
11505   int element = MovingOrBlocked2Element(x, y);
11506   struct ElementInfo *ei = &element_info[element];
11507   struct ElementChangeInfo *change = &ei->change_page[page];
11508
11509 #ifdef DEBUG
11510   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11511   {
11512     printf("\n\n");
11513     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11514            x, y, element, element_info[element].token_name);
11515     printf("HandleElementChange(): This should never happen!\n");
11516     printf("\n\n");
11517   }
11518 #endif
11519
11520   /* this can happen with classic bombs on walkable, changing elements */
11521   if (!CAN_CHANGE(element))
11522   {
11523 #if 0
11524     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11525       ChangeDelay[x][y] = 0;
11526 #endif
11527
11528     return;
11529   }
11530
11531   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11532   {
11533     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11534
11535     ResetGfxAnimation(x, y);
11536     ResetRandomAnimationValue(x, y);
11537
11538     if (change->pre_change_function)
11539       change->pre_change_function(x, y);
11540   }
11541
11542   ChangeDelay[x][y]--;
11543
11544   if (ChangeDelay[x][y] != 0)           /* continue element change */
11545   {
11546     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11547
11548     if (IS_ANIMATED(graphic))
11549       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11550
11551     if (change->change_function)
11552       change->change_function(x, y);
11553   }
11554   else                                  /* finish element change */
11555   {
11556     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11557     {
11558       page = ChangePage[x][y];
11559       ChangePage[x][y] = -1;
11560
11561       change = &ei->change_page[page];
11562     }
11563
11564     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11565     {
11566       ChangeDelay[x][y] = 1;            /* try change after next move step */
11567       ChangePage[x][y] = page;          /* remember page to use for change */
11568
11569       return;
11570     }
11571
11572     if (ChangeElement(x, y, element, page))
11573     {
11574       if (change->post_change_function)
11575         change->post_change_function(x, y);
11576     }
11577   }
11578 }
11579
11580 #endif
11581
11582 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11583                                               int trigger_element,
11584                                               int trigger_event,
11585                                               int trigger_player,
11586                                               int trigger_side,
11587                                               int trigger_page)
11588 {
11589   boolean change_done_any = FALSE;
11590   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11591   int i;
11592
11593   if (!(trigger_events[trigger_element][trigger_event]))
11594     return FALSE;
11595
11596 #if 0
11597   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11598          trigger_event, recursion_loop_depth, recursion_loop_detected,
11599          recursion_loop_element, EL_NAME(recursion_loop_element));
11600 #endif
11601
11602   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11603
11604   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11605   {
11606     int element = EL_CUSTOM_START + i;
11607     boolean change_done = FALSE;
11608     int p;
11609
11610     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11611         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11612       continue;
11613
11614     for (p = 0; p < element_info[element].num_change_pages; p++)
11615     {
11616       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11617
11618       if (change->can_change_or_has_action &&
11619           change->has_event[trigger_event] &&
11620           change->trigger_side & trigger_side &&
11621           change->trigger_player & trigger_player &&
11622           change->trigger_page & trigger_page_bits &&
11623           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11624       {
11625         change->actual_trigger_element = trigger_element;
11626         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11627         change->actual_trigger_player_bits = trigger_player;
11628         change->actual_trigger_side = trigger_side;
11629         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11630         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11631
11632 #if 0
11633         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11634                element, EL_NAME(element), p);
11635 #endif
11636
11637         if ((change->can_change && !change_done) || change->has_action)
11638         {
11639           int x, y;
11640
11641           SCAN_PLAYFIELD(x, y)
11642           {
11643             if (Feld[x][y] == element)
11644             {
11645               if (change->can_change && !change_done)
11646               {
11647 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11648                 /* if element already changed in this frame, not only prevent
11649                    another element change (checked in ChangeElement()), but
11650                    also prevent additional element actions for this element */
11651
11652                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11653                     !level.use_action_after_change_bug)
11654                   continue;
11655 #endif
11656
11657 #if 0
11658                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11659                        element, EL_NAME(element), p);
11660 #endif
11661
11662                 ChangeDelay[x][y] = 1;
11663                 ChangeEvent[x][y] = trigger_event;
11664
11665                 HandleElementChange(x, y, p);
11666               }
11667 #if USE_NEW_DELAYED_ACTION
11668               else if (change->has_action)
11669               {
11670 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11671                 /* if element already changed in this frame, not only prevent
11672                    another element change (checked in ChangeElement()), but
11673                    also prevent additional element actions for this element */
11674
11675                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11676                     !level.use_action_after_change_bug)
11677                   continue;
11678 #endif
11679
11680
11681 #if 0
11682                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11683                        element, EL_NAME(element), p);
11684 #endif
11685
11686                 ExecuteCustomElementAction(x, y, element, p);
11687                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11688               }
11689 #else
11690               if (change->has_action)
11691               {
11692                 ExecuteCustomElementAction(x, y, element, p);
11693                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11694               }
11695 #endif
11696             }
11697           }
11698
11699           if (change->can_change)
11700           {
11701             change_done = TRUE;
11702             change_done_any = TRUE;
11703
11704 #if 0
11705             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11706                    element, EL_NAME(element), p);
11707 #endif
11708
11709           }
11710         }
11711       }
11712     }
11713   }
11714
11715   RECURSION_LOOP_DETECTION_END();
11716
11717   return change_done_any;
11718 }
11719
11720 static boolean CheckElementChangeExt(int x, int y,
11721                                      int element,
11722                                      int trigger_element,
11723                                      int trigger_event,
11724                                      int trigger_player,
11725                                      int trigger_side)
11726 {
11727   boolean change_done = FALSE;
11728   int p;
11729
11730   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11731       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11732     return FALSE;
11733
11734   if (Feld[x][y] == EL_BLOCKED)
11735   {
11736     Blocked2Moving(x, y, &x, &y);
11737     element = Feld[x][y];
11738   }
11739
11740 #if 0
11741   /* check if element has already changed */
11742   if (Feld[x][y] != element)
11743     return FALSE;
11744 #else
11745   /* check if element has already changed or is about to change after moving */
11746   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11747        Feld[x][y] != element) ||
11748
11749       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11750        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11751         ChangePage[x][y] != -1)))
11752     return FALSE;
11753 #endif
11754
11755 #if 0
11756   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11757          trigger_event, recursion_loop_depth, recursion_loop_detected,
11758          recursion_loop_element, EL_NAME(recursion_loop_element));
11759 #endif
11760
11761   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11762
11763 #if 0
11764   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11765 #endif
11766
11767   for (p = 0; p < element_info[element].num_change_pages; p++)
11768   {
11769     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11770
11771     /* check trigger element for all events where the element that is checked
11772        for changing interacts with a directly adjacent element -- this is
11773        different to element changes that affect other elements to change on the
11774        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11775     boolean check_trigger_element =
11776       (trigger_event == CE_TOUCHING_X ||
11777        trigger_event == CE_HITTING_X ||
11778        trigger_event == CE_HIT_BY_X ||
11779 #if 1
11780        /* this one was forgotten until 3.2.3 */
11781        trigger_event == CE_DIGGING_X);
11782 #endif
11783
11784     if (change->can_change_or_has_action &&
11785         change->has_event[trigger_event] &&
11786         change->trigger_side & trigger_side &&
11787         change->trigger_player & trigger_player &&
11788         (!check_trigger_element ||
11789          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11790     {
11791       change->actual_trigger_element = trigger_element;
11792       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11793       change->actual_trigger_player_bits = trigger_player;
11794       change->actual_trigger_side = trigger_side;
11795       change->actual_trigger_ce_value = CustomValue[x][y];
11796       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11797
11798       /* special case: trigger element not at (x,y) position for some events */
11799       if (check_trigger_element)
11800       {
11801         static struct
11802         {
11803           int dx, dy;
11804         } move_xy[] =
11805           {
11806             {  0,  0 },
11807             { -1,  0 },
11808             { +1,  0 },
11809             {  0,  0 },
11810             {  0, -1 },
11811             {  0,  0 }, { 0, 0 }, { 0, 0 },
11812             {  0, +1 }
11813           };
11814
11815         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11816         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11817
11818         change->actual_trigger_ce_value = CustomValue[xx][yy];
11819         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11820       }
11821
11822       if (change->can_change && !change_done)
11823       {
11824         ChangeDelay[x][y] = 1;
11825         ChangeEvent[x][y] = trigger_event;
11826
11827         HandleElementChange(x, y, p);
11828
11829         change_done = TRUE;
11830       }
11831 #if USE_NEW_DELAYED_ACTION
11832       else if (change->has_action)
11833       {
11834         ExecuteCustomElementAction(x, y, element, p);
11835         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11836       }
11837 #else
11838       if (change->has_action)
11839       {
11840         ExecuteCustomElementAction(x, y, element, p);
11841         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11842       }
11843 #endif
11844     }
11845   }
11846
11847   RECURSION_LOOP_DETECTION_END();
11848
11849   return change_done;
11850 }
11851
11852 static void PlayPlayerSound(struct PlayerInfo *player)
11853 {
11854   int jx = player->jx, jy = player->jy;
11855   int sound_element = player->artwork_element;
11856   int last_action = player->last_action_waiting;
11857   int action = player->action_waiting;
11858
11859   if (player->is_waiting)
11860   {
11861     if (action != last_action)
11862       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11863     else
11864       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11865   }
11866   else
11867   {
11868     if (action != last_action)
11869       StopSound(element_info[sound_element].sound[last_action]);
11870
11871     if (last_action == ACTION_SLEEPING)
11872       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11873   }
11874 }
11875
11876 static void PlayAllPlayersSound()
11877 {
11878   int i;
11879
11880   for (i = 0; i < MAX_PLAYERS; i++)
11881     if (stored_player[i].active)
11882       PlayPlayerSound(&stored_player[i]);
11883 }
11884
11885 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11886 {
11887   boolean last_waiting = player->is_waiting;
11888   int move_dir = player->MovDir;
11889
11890   player->dir_waiting = move_dir;
11891   player->last_action_waiting = player->action_waiting;
11892
11893   if (is_waiting)
11894   {
11895     if (!last_waiting)          /* not waiting -> waiting */
11896     {
11897       player->is_waiting = TRUE;
11898
11899       player->frame_counter_bored =
11900         FrameCounter +
11901         game.player_boring_delay_fixed +
11902         GetSimpleRandom(game.player_boring_delay_random);
11903       player->frame_counter_sleeping =
11904         FrameCounter +
11905         game.player_sleeping_delay_fixed +
11906         GetSimpleRandom(game.player_sleeping_delay_random);
11907
11908       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11909     }
11910
11911     if (game.player_sleeping_delay_fixed +
11912         game.player_sleeping_delay_random > 0 &&
11913         player->anim_delay_counter == 0 &&
11914         player->post_delay_counter == 0 &&
11915         FrameCounter >= player->frame_counter_sleeping)
11916       player->is_sleeping = TRUE;
11917     else if (game.player_boring_delay_fixed +
11918              game.player_boring_delay_random > 0 &&
11919              FrameCounter >= player->frame_counter_bored)
11920       player->is_bored = TRUE;
11921
11922     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11923                               player->is_bored ? ACTION_BORING :
11924                               ACTION_WAITING);
11925
11926     if (player->is_sleeping && player->use_murphy)
11927     {
11928       /* special case for sleeping Murphy when leaning against non-free tile */
11929
11930       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11931           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11932            !IS_MOVING(player->jx - 1, player->jy)))
11933         move_dir = MV_LEFT;
11934       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11935                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11936                 !IS_MOVING(player->jx + 1, player->jy)))
11937         move_dir = MV_RIGHT;
11938       else
11939         player->is_sleeping = FALSE;
11940
11941       player->dir_waiting = move_dir;
11942     }
11943
11944     if (player->is_sleeping)
11945     {
11946       if (player->num_special_action_sleeping > 0)
11947       {
11948         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11949         {
11950           int last_special_action = player->special_action_sleeping;
11951           int num_special_action = player->num_special_action_sleeping;
11952           int special_action =
11953             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11954              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11955              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11956              last_special_action + 1 : ACTION_SLEEPING);
11957           int special_graphic =
11958             el_act_dir2img(player->artwork_element, special_action, move_dir);
11959
11960           player->anim_delay_counter =
11961             graphic_info[special_graphic].anim_delay_fixed +
11962             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11963           player->post_delay_counter =
11964             graphic_info[special_graphic].post_delay_fixed +
11965             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11966
11967           player->special_action_sleeping = special_action;
11968         }
11969
11970         if (player->anim_delay_counter > 0)
11971         {
11972           player->action_waiting = player->special_action_sleeping;
11973           player->anim_delay_counter--;
11974         }
11975         else if (player->post_delay_counter > 0)
11976         {
11977           player->post_delay_counter--;
11978         }
11979       }
11980     }
11981     else if (player->is_bored)
11982     {
11983       if (player->num_special_action_bored > 0)
11984       {
11985         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11986         {
11987           int special_action =
11988             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11989           int special_graphic =
11990             el_act_dir2img(player->artwork_element, special_action, move_dir);
11991
11992           player->anim_delay_counter =
11993             graphic_info[special_graphic].anim_delay_fixed +
11994             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11995           player->post_delay_counter =
11996             graphic_info[special_graphic].post_delay_fixed +
11997             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11998
11999           player->special_action_bored = special_action;
12000         }
12001
12002         if (player->anim_delay_counter > 0)
12003         {
12004           player->action_waiting = player->special_action_bored;
12005           player->anim_delay_counter--;
12006         }
12007         else if (player->post_delay_counter > 0)
12008         {
12009           player->post_delay_counter--;
12010         }
12011       }
12012     }
12013   }
12014   else if (last_waiting)        /* waiting -> not waiting */
12015   {
12016     player->is_waiting = FALSE;
12017     player->is_bored = FALSE;
12018     player->is_sleeping = FALSE;
12019
12020     player->frame_counter_bored = -1;
12021     player->frame_counter_sleeping = -1;
12022
12023     player->anim_delay_counter = 0;
12024     player->post_delay_counter = 0;
12025
12026     player->dir_waiting = player->MovDir;
12027     player->action_waiting = ACTION_DEFAULT;
12028
12029     player->special_action_bored = ACTION_DEFAULT;
12030     player->special_action_sleeping = ACTION_DEFAULT;
12031   }
12032 }
12033
12034 static void CheckSingleStepMode(struct PlayerInfo *player)
12035 {
12036   if (tape.single_step && tape.recording && !tape.pausing)
12037   {
12038     /* as it is called "single step mode", just return to pause mode when the
12039        player stopped moving after one tile (or never starts moving at all) */
12040     if (!player->is_moving && !player->is_pushing)
12041     {
12042       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12043       SnapField(player, 0, 0);                  /* stop snapping */
12044     }
12045   }
12046 }
12047
12048 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12049 {
12050   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12051   int left      = player_action & JOY_LEFT;
12052   int right     = player_action & JOY_RIGHT;
12053   int up        = player_action & JOY_UP;
12054   int down      = player_action & JOY_DOWN;
12055   int button1   = player_action & JOY_BUTTON_1;
12056   int button2   = player_action & JOY_BUTTON_2;
12057   int dx        = (left ? -1 : right ? 1 : 0);
12058   int dy        = (up   ? -1 : down  ? 1 : 0);
12059
12060   if (!player->active || tape.pausing)
12061     return 0;
12062
12063   if (player_action)
12064   {
12065     if (button1)
12066       snapped = SnapField(player, dx, dy);
12067     else
12068     {
12069       if (button2)
12070         dropped = DropElement(player);
12071
12072       moved = MovePlayer(player, dx, dy);
12073     }
12074
12075     CheckSingleStepMode(player);
12076
12077     SetPlayerWaiting(player, FALSE);
12078
12079     return player_action;
12080   }
12081   else
12082   {
12083     /* no actions for this player (no input at player's configured device) */
12084
12085     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12086     SnapField(player, 0, 0);
12087     CheckGravityMovementWhenNotMoving(player);
12088
12089     if (player->MovPos == 0)
12090       SetPlayerWaiting(player, TRUE);
12091
12092     if (player->MovPos == 0)    /* needed for tape.playing */
12093       player->is_moving = FALSE;
12094
12095     player->is_dropping = FALSE;
12096     player->is_dropping_pressed = FALSE;
12097     player->drop_pressed_delay = 0;
12098
12099     CheckSingleStepMode(player);
12100
12101     return 0;
12102   }
12103 }
12104
12105 static void CheckLevelTime()
12106 {
12107   int i;
12108
12109   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12110   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12111   {
12112     if (level.native_em_level->lev->home == 0)  /* all players at home */
12113     {
12114       PlayerWins(local_player);
12115
12116       AllPlayersGone = TRUE;
12117
12118       level.native_em_level->lev->home = -1;
12119     }
12120
12121     if (level.native_em_level->ply[0]->alive == 0 &&
12122         level.native_em_level->ply[1]->alive == 0 &&
12123         level.native_em_level->ply[2]->alive == 0 &&
12124         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12125       AllPlayersGone = TRUE;
12126   }
12127   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12128   {
12129     if (game_sp.LevelSolved &&
12130         !game_sp.GameOver)                              /* game won */
12131     {
12132       PlayerWins(local_player);
12133
12134       game_sp.GameOver = TRUE;
12135
12136       AllPlayersGone = TRUE;
12137     }
12138
12139     if (game_sp.GameOver)                               /* game lost */
12140       AllPlayersGone = TRUE;
12141   }
12142
12143   if (TimeFrames >= FRAMES_PER_SECOND)
12144   {
12145     TimeFrames = 0;
12146     TapeTime++;
12147
12148     for (i = 0; i < MAX_PLAYERS; i++)
12149     {
12150       struct PlayerInfo *player = &stored_player[i];
12151
12152       if (SHIELD_ON(player))
12153       {
12154         player->shield_normal_time_left--;
12155
12156         if (player->shield_deadly_time_left > 0)
12157           player->shield_deadly_time_left--;
12158       }
12159     }
12160
12161     if (!local_player->LevelSolved && !level.use_step_counter)
12162     {
12163       TimePlayed++;
12164
12165       if (TimeLeft > 0)
12166       {
12167         TimeLeft--;
12168
12169         if (TimeLeft <= 10 && setup.time_limit)
12170           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12171
12172 #if 1
12173         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12174
12175         DisplayGameControlValues();
12176 #else
12177         DrawGameValue_Time(TimeLeft);
12178 #endif
12179
12180         if (!TimeLeft && setup.time_limit)
12181         {
12182           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12183             level.native_em_level->lev->killed_out_of_time = TRUE;
12184           else
12185             for (i = 0; i < MAX_PLAYERS; i++)
12186               KillPlayer(&stored_player[i]);
12187         }
12188       }
12189 #if 1
12190       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12191       {
12192         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12193
12194         DisplayGameControlValues();
12195       }
12196 #else
12197       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12198         DrawGameValue_Time(TimePlayed);
12199 #endif
12200
12201       level.native_em_level->lev->time =
12202         (level.time == 0 ? TimePlayed : TimeLeft);
12203     }
12204
12205     if (tape.recording || tape.playing)
12206       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12207   }
12208
12209 #if 1
12210   UpdateAndDisplayGameControlValues();
12211 #else
12212   UpdateGameDoorValues();
12213   DrawGameDoorValues();
12214 #endif
12215 }
12216
12217 void AdvanceFrameAndPlayerCounters(int player_nr)
12218 {
12219   int i;
12220
12221   /* advance frame counters (global frame counter and time frame counter) */
12222   FrameCounter++;
12223   TimeFrames++;
12224
12225   /* advance player counters (counters for move delay, move animation etc.) */
12226   for (i = 0; i < MAX_PLAYERS; i++)
12227   {
12228     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12229     int move_delay_value = stored_player[i].move_delay_value;
12230     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12231
12232     if (!advance_player_counters)       /* not all players may be affected */
12233       continue;
12234
12235 #if USE_NEW_PLAYER_ANIM
12236     if (move_frames == 0)       /* less than one move per game frame */
12237     {
12238       int stepsize = TILEX / move_delay_value;
12239       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12240       int count = (stored_player[i].is_moving ?
12241                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12242
12243       if (count % delay == 0)
12244         move_frames = 1;
12245     }
12246 #endif
12247
12248     stored_player[i].Frame += move_frames;
12249
12250     if (stored_player[i].MovPos != 0)
12251       stored_player[i].StepFrame += move_frames;
12252
12253     if (stored_player[i].move_delay > 0)
12254       stored_player[i].move_delay--;
12255
12256     /* due to bugs in previous versions, counter must count up, not down */
12257     if (stored_player[i].push_delay != -1)
12258       stored_player[i].push_delay++;
12259
12260     if (stored_player[i].drop_delay > 0)
12261       stored_player[i].drop_delay--;
12262
12263     if (stored_player[i].is_dropping_pressed)
12264       stored_player[i].drop_pressed_delay++;
12265   }
12266 }
12267
12268 void StartGameActions(boolean init_network_game, boolean record_tape,
12269                       long random_seed)
12270 {
12271   unsigned long new_random_seed = InitRND(random_seed);
12272
12273   if (record_tape)
12274     TapeStartRecording(new_random_seed);
12275
12276 #if defined(NETWORK_AVALIABLE)
12277   if (init_network_game)
12278   {
12279     SendToServer_StartPlaying();
12280
12281     return;
12282   }
12283 #endif
12284
12285   InitGame();
12286 }
12287
12288 void GameActions()
12289 {
12290   static unsigned long game_frame_delay = 0;
12291   unsigned long game_frame_delay_value;
12292   byte *recorded_player_action;
12293   byte summarized_player_action = 0;
12294   byte tape_action[MAX_PLAYERS];
12295   int i;
12296
12297   /* detect endless loops, caused by custom element programming */
12298   if (recursion_loop_detected && recursion_loop_depth == 0)
12299   {
12300     char *message = getStringCat3("Internal Error ! Element ",
12301                                   EL_NAME(recursion_loop_element),
12302                                   " caused endless loop ! Quit the game ?");
12303
12304     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12305           EL_NAME(recursion_loop_element));
12306
12307     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12308
12309     recursion_loop_detected = FALSE;    /* if game should be continued */
12310
12311     free(message);
12312
12313     return;
12314   }
12315
12316   if (game.restart_level)
12317     StartGameActions(options.network, setup.autorecord, level.random_seed);
12318
12319   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12320   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12321   {
12322     if (level.native_em_level->lev->home == 0)  /* all players at home */
12323     {
12324       PlayerWins(local_player);
12325
12326       AllPlayersGone = TRUE;
12327
12328       level.native_em_level->lev->home = -1;
12329     }
12330
12331     if (level.native_em_level->ply[0]->alive == 0 &&
12332         level.native_em_level->ply[1]->alive == 0 &&
12333         level.native_em_level->ply[2]->alive == 0 &&
12334         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12335       AllPlayersGone = TRUE;
12336   }
12337   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12338   {
12339     if (game_sp.LevelSolved &&
12340         !game_sp.GameOver)                              /* game won */
12341     {
12342       PlayerWins(local_player);
12343
12344       game_sp.GameOver = TRUE;
12345
12346       AllPlayersGone = TRUE;
12347     }
12348
12349     if (game_sp.GameOver)                               /* game lost */
12350       AllPlayersGone = TRUE;
12351   }
12352
12353   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12354     GameWon();
12355
12356   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12357     TapeStop();
12358
12359   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12360     return;
12361
12362   game_frame_delay_value =
12363     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12364
12365   if (tape.playing && tape.warp_forward && !tape.pausing)
12366     game_frame_delay_value = 0;
12367
12368   /* ---------- main game synchronization point ---------- */
12369
12370   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12371
12372   if (network_playing && !network_player_action_received)
12373   {
12374     /* try to get network player actions in time */
12375
12376 #if defined(NETWORK_AVALIABLE)
12377     /* last chance to get network player actions without main loop delay */
12378     HandleNetworking();
12379 #endif
12380
12381     /* game was quit by network peer */
12382     if (game_status != GAME_MODE_PLAYING)
12383       return;
12384
12385     if (!network_player_action_received)
12386       return;           /* failed to get network player actions in time */
12387
12388     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12389   }
12390
12391   if (tape.pausing)
12392     return;
12393
12394   /* at this point we know that we really continue executing the game */
12395
12396   network_player_action_received = FALSE;
12397
12398   /* when playing tape, read previously recorded player input from tape data */
12399   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12400
12401 #if 1
12402   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12403   if (tape.pausing)
12404     return;
12405 #endif
12406
12407   if (tape.set_centered_player)
12408   {
12409     game.centered_player_nr_next = tape.centered_player_nr_next;
12410     game.set_centered_player = TRUE;
12411   }
12412
12413   for (i = 0; i < MAX_PLAYERS; i++)
12414   {
12415     summarized_player_action |= stored_player[i].action;
12416
12417     if (!network_playing)
12418       stored_player[i].effective_action = stored_player[i].action;
12419   }
12420
12421 #if defined(NETWORK_AVALIABLE)
12422   if (network_playing)
12423     SendToServer_MovePlayer(summarized_player_action);
12424 #endif
12425
12426   if (!options.network && !setup.team_mode)
12427     local_player->effective_action = summarized_player_action;
12428
12429   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12430   {
12431     for (i = 0; i < MAX_PLAYERS; i++)
12432       stored_player[i].effective_action =
12433         (i == game.centered_player_nr ? summarized_player_action : 0);
12434   }
12435
12436   if (recorded_player_action != NULL)
12437     for (i = 0; i < MAX_PLAYERS; i++)
12438       stored_player[i].effective_action = recorded_player_action[i];
12439
12440   for (i = 0; i < MAX_PLAYERS; i++)
12441   {
12442     tape_action[i] = stored_player[i].effective_action;
12443
12444     /* (this can only happen in the R'n'D game engine) */
12445     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12446       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12447   }
12448
12449   /* only record actions from input devices, but not programmed actions */
12450   if (tape.recording)
12451     TapeRecordAction(tape_action);
12452
12453 #if USE_NEW_PLAYER_ASSIGNMENTS
12454   {
12455     byte mapped_action[MAX_PLAYERS];
12456
12457     for (i = 0; i < MAX_PLAYERS; i++)
12458       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12459
12460     for (i = 0; i < MAX_PLAYERS; i++)
12461       stored_player[i].effective_action = mapped_action[i];
12462   }
12463 #endif
12464
12465   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12466   {
12467     GameActions_EM_Main();
12468   }
12469   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12470   {
12471     GameActions_SP_Main();
12472   }
12473   else
12474   {
12475     GameActions_RND();
12476   }
12477 }
12478
12479 void GameActions_EM_Main()
12480 {
12481   byte effective_action[MAX_PLAYERS];
12482   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12483   int i;
12484
12485   for (i = 0; i < MAX_PLAYERS; i++)
12486     effective_action[i] = stored_player[i].effective_action;
12487
12488   GameActions_EM(effective_action, warp_mode);
12489
12490   CheckLevelTime();
12491
12492   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12493 }
12494
12495 void GameActions_SP_Main()
12496 {
12497   byte effective_action[MAX_PLAYERS];
12498   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12499   int i;
12500
12501   for (i = 0; i < MAX_PLAYERS; i++)
12502     effective_action[i] = stored_player[i].effective_action;
12503
12504   GameActions_SP(effective_action, warp_mode);
12505
12506   CheckLevelTime();
12507
12508   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12509 }
12510
12511 void GameActions_RND()
12512 {
12513   int magic_wall_x = 0, magic_wall_y = 0;
12514   int i, x, y, element, graphic;
12515
12516   InitPlayfieldScanModeVars();
12517
12518 #if USE_ONE_MORE_CHANGE_PER_FRAME
12519   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12520   {
12521     SCAN_PLAYFIELD(x, y)
12522     {
12523       ChangeCount[x][y] = 0;
12524       ChangeEvent[x][y] = -1;
12525     }
12526   }
12527 #endif
12528
12529   if (game.set_centered_player)
12530   {
12531     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12532
12533     /* switching to "all players" only possible if all players fit to screen */
12534     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12535     {
12536       game.centered_player_nr_next = game.centered_player_nr;
12537       game.set_centered_player = FALSE;
12538     }
12539
12540     /* do not switch focus to non-existing (or non-active) player */
12541     if (game.centered_player_nr_next >= 0 &&
12542         !stored_player[game.centered_player_nr_next].active)
12543     {
12544       game.centered_player_nr_next = game.centered_player_nr;
12545       game.set_centered_player = FALSE;
12546     }
12547   }
12548
12549   if (game.set_centered_player &&
12550       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12551   {
12552     int sx, sy;
12553
12554     if (game.centered_player_nr_next == -1)
12555     {
12556       setScreenCenteredToAllPlayers(&sx, &sy);
12557     }
12558     else
12559     {
12560       sx = stored_player[game.centered_player_nr_next].jx;
12561       sy = stored_player[game.centered_player_nr_next].jy;
12562     }
12563
12564     game.centered_player_nr = game.centered_player_nr_next;
12565     game.set_centered_player = FALSE;
12566
12567     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12568     DrawGameDoorValues();
12569   }
12570
12571   for (i = 0; i < MAX_PLAYERS; i++)
12572   {
12573     int actual_player_action = stored_player[i].effective_action;
12574
12575 #if 1
12576     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12577        - rnd_equinox_tetrachloride 048
12578        - rnd_equinox_tetrachloride_ii 096
12579        - rnd_emanuel_schmieg 002
12580        - doctor_sloan_ww 001, 020
12581     */
12582     if (stored_player[i].MovPos == 0)
12583       CheckGravityMovement(&stored_player[i]);
12584 #endif
12585
12586     /* overwrite programmed action with tape action */
12587     if (stored_player[i].programmed_action)
12588       actual_player_action = stored_player[i].programmed_action;
12589
12590     PlayerActions(&stored_player[i], actual_player_action);
12591
12592     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12593   }
12594
12595   ScrollScreen(NULL, SCROLL_GO_ON);
12596
12597   /* for backwards compatibility, the following code emulates a fixed bug that
12598      occured when pushing elements (causing elements that just made their last
12599      pushing step to already (if possible) make their first falling step in the
12600      same game frame, which is bad); this code is also needed to use the famous
12601      "spring push bug" which is used in older levels and might be wanted to be
12602      used also in newer levels, but in this case the buggy pushing code is only
12603      affecting the "spring" element and no other elements */
12604
12605   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12606   {
12607     for (i = 0; i < MAX_PLAYERS; i++)
12608     {
12609       struct PlayerInfo *player = &stored_player[i];
12610       int x = player->jx;
12611       int y = player->jy;
12612
12613       if (player->active && player->is_pushing && player->is_moving &&
12614           IS_MOVING(x, y) &&
12615           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12616            Feld[x][y] == EL_SPRING))
12617       {
12618         ContinueMoving(x, y);
12619
12620         /* continue moving after pushing (this is actually a bug) */
12621         if (!IS_MOVING(x, y))
12622           Stop[x][y] = FALSE;
12623       }
12624     }
12625   }
12626
12627 #if 0
12628   debug_print_timestamp(0, "start main loop profiling");
12629 #endif
12630
12631   SCAN_PLAYFIELD(x, y)
12632   {
12633     ChangeCount[x][y] = 0;
12634     ChangeEvent[x][y] = -1;
12635
12636     /* this must be handled before main playfield loop */
12637     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12638     {
12639       MovDelay[x][y]--;
12640       if (MovDelay[x][y] <= 0)
12641         RemoveField(x, y);
12642     }
12643
12644 #if USE_NEW_SNAP_DELAY
12645     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12646     {
12647       MovDelay[x][y]--;
12648       if (MovDelay[x][y] <= 0)
12649       {
12650         RemoveField(x, y);
12651         TEST_DrawLevelField(x, y);
12652
12653         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12654       }
12655     }
12656 #endif
12657
12658 #if DEBUG
12659     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12660     {
12661       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12662       printf("GameActions(): This should never happen!\n");
12663
12664       ChangePage[x][y] = -1;
12665     }
12666 #endif
12667
12668     Stop[x][y] = FALSE;
12669     if (WasJustMoving[x][y] > 0)
12670       WasJustMoving[x][y]--;
12671     if (WasJustFalling[x][y] > 0)
12672       WasJustFalling[x][y]--;
12673     if (CheckCollision[x][y] > 0)
12674       CheckCollision[x][y]--;
12675     if (CheckImpact[x][y] > 0)
12676       CheckImpact[x][y]--;
12677
12678     GfxFrame[x][y]++;
12679
12680     /* reset finished pushing action (not done in ContinueMoving() to allow
12681        continuous pushing animation for elements with zero push delay) */
12682     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12683     {
12684       ResetGfxAnimation(x, y);
12685       TEST_DrawLevelField(x, y);
12686     }
12687
12688 #if DEBUG
12689     if (IS_BLOCKED(x, y))
12690     {
12691       int oldx, oldy;
12692
12693       Blocked2Moving(x, y, &oldx, &oldy);
12694       if (!IS_MOVING(oldx, oldy))
12695       {
12696         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12697         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12698         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12699         printf("GameActions(): This should never happen!\n");
12700       }
12701     }
12702 #endif
12703   }
12704
12705 #if 0
12706   debug_print_timestamp(0, "- time for pre-main loop:");
12707 #endif
12708
12709 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12710   SCAN_PLAYFIELD(x, y)
12711   {
12712     element = Feld[x][y];
12713     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12714
12715 #if 1
12716     {
12717 #if 1
12718       int element2 = element;
12719       int graphic2 = graphic;
12720 #else
12721       int element2 = Feld[x][y];
12722       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12723 #endif
12724       int last_gfx_frame = GfxFrame[x][y];
12725
12726       if (graphic_info[graphic2].anim_global_sync)
12727         GfxFrame[x][y] = FrameCounter;
12728       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12729         GfxFrame[x][y] = CustomValue[x][y];
12730       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12731         GfxFrame[x][y] = element_info[element2].collect_score;
12732       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12733         GfxFrame[x][y] = ChangeDelay[x][y];
12734
12735       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12736         DrawLevelGraphicAnimation(x, y, graphic2);
12737     }
12738 #else
12739     ResetGfxFrame(x, y, TRUE);
12740 #endif
12741
12742 #if 1
12743     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12744         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12745       ResetRandomAnimationValue(x, y);
12746 #endif
12747
12748 #if 1
12749     SetRandomAnimationValue(x, y);
12750 #endif
12751
12752 #if 1
12753     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12754 #endif
12755   }
12756 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12757
12758 #if 0
12759   debug_print_timestamp(0, "- time for TEST loop:     -->");
12760 #endif
12761
12762   SCAN_PLAYFIELD(x, y)
12763   {
12764     element = Feld[x][y];
12765     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12766
12767     ResetGfxFrame(x, y, TRUE);
12768
12769     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12770         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12771       ResetRandomAnimationValue(x, y);
12772
12773     SetRandomAnimationValue(x, y);
12774
12775     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12776
12777     if (IS_INACTIVE(element))
12778     {
12779       if (IS_ANIMATED(graphic))
12780         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12781
12782       continue;
12783     }
12784
12785     /* this may take place after moving, so 'element' may have changed */
12786     if (IS_CHANGING(x, y) &&
12787         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12788     {
12789       int page = element_info[element].event_page_nr[CE_DELAY];
12790
12791 #if 1
12792       HandleElementChange(x, y, page);
12793 #else
12794       if (CAN_CHANGE(element))
12795         HandleElementChange(x, y, page);
12796
12797       if (HAS_ACTION(element))
12798         ExecuteCustomElementAction(x, y, element, page);
12799 #endif
12800
12801       element = Feld[x][y];
12802       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12803     }
12804
12805 #if 0   // ---------------------------------------------------------------------
12806
12807     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12808     {
12809       StartMoving(x, y);
12810
12811       element = Feld[x][y];
12812       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12813
12814       if (IS_ANIMATED(graphic) &&
12815           !IS_MOVING(x, y) &&
12816           !Stop[x][y])
12817         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12818
12819       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12820         TEST_DrawTwinkleOnField(x, y);
12821     }
12822     else if (IS_MOVING(x, y))
12823       ContinueMoving(x, y);
12824     else
12825     {
12826       switch (element)
12827       {
12828         case EL_ACID:
12829         case EL_EXIT_OPEN:
12830         case EL_EM_EXIT_OPEN:
12831         case EL_SP_EXIT_OPEN:
12832         case EL_STEEL_EXIT_OPEN:
12833         case EL_EM_STEEL_EXIT_OPEN:
12834         case EL_SP_TERMINAL:
12835         case EL_SP_TERMINAL_ACTIVE:
12836         case EL_EXTRA_TIME:
12837         case EL_SHIELD_NORMAL:
12838         case EL_SHIELD_DEADLY:
12839           if (IS_ANIMATED(graphic))
12840             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12841           break;
12842
12843         case EL_DYNAMITE_ACTIVE:
12844         case EL_EM_DYNAMITE_ACTIVE:
12845         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12846         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12847         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12848         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12849         case EL_SP_DISK_RED_ACTIVE:
12850           CheckDynamite(x, y);
12851           break;
12852
12853         case EL_AMOEBA_GROWING:
12854           AmoebeWaechst(x, y);
12855           break;
12856
12857         case EL_AMOEBA_SHRINKING:
12858           AmoebaDisappearing(x, y);
12859           break;
12860
12861 #if !USE_NEW_AMOEBA_CODE
12862         case EL_AMOEBA_WET:
12863         case EL_AMOEBA_DRY:
12864         case EL_AMOEBA_FULL:
12865         case EL_BD_AMOEBA:
12866         case EL_EMC_DRIPPER:
12867           AmoebeAbleger(x, y);
12868           break;
12869 #endif
12870
12871         case EL_GAME_OF_LIFE:
12872         case EL_BIOMAZE:
12873           Life(x, y);
12874           break;
12875
12876         case EL_EXIT_CLOSED:
12877           CheckExit(x, y);
12878           break;
12879
12880         case EL_EM_EXIT_CLOSED:
12881           CheckExitEM(x, y);
12882           break;
12883
12884         case EL_STEEL_EXIT_CLOSED:
12885           CheckExitSteel(x, y);
12886           break;
12887
12888         case EL_EM_STEEL_EXIT_CLOSED:
12889           CheckExitSteelEM(x, y);
12890           break;
12891
12892         case EL_SP_EXIT_CLOSED:
12893           CheckExitSP(x, y);
12894           break;
12895
12896         case EL_EXPANDABLE_WALL_GROWING:
12897         case EL_EXPANDABLE_STEELWALL_GROWING:
12898           MauerWaechst(x, y);
12899           break;
12900
12901         case EL_EXPANDABLE_WALL:
12902         case EL_EXPANDABLE_WALL_HORIZONTAL:
12903         case EL_EXPANDABLE_WALL_VERTICAL:
12904         case EL_EXPANDABLE_WALL_ANY:
12905         case EL_BD_EXPANDABLE_WALL:
12906           MauerAbleger(x, y);
12907           break;
12908
12909         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12910         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12911         case EL_EXPANDABLE_STEELWALL_ANY:
12912           MauerAblegerStahl(x, y);
12913           break;
12914
12915         case EL_FLAMES:
12916           CheckForDragon(x, y);
12917           break;
12918
12919         case EL_EXPLOSION:
12920           break;
12921
12922         case EL_ELEMENT_SNAPPING:
12923         case EL_DIAGONAL_SHRINKING:
12924         case EL_DIAGONAL_GROWING:
12925         {
12926           graphic =
12927             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12928
12929           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12930           break;
12931         }
12932
12933         default:
12934           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12935             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12936           break;
12937       }
12938     }
12939
12940 #else   // ---------------------------------------------------------------------
12941
12942     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12943     {
12944       StartMoving(x, y);
12945
12946       element = Feld[x][y];
12947       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12948
12949       if (IS_ANIMATED(graphic) &&
12950           !IS_MOVING(x, y) &&
12951           !Stop[x][y])
12952         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12953
12954       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12955         TEST_DrawTwinkleOnField(x, y);
12956     }
12957     else if ((element == EL_ACID ||
12958               element == EL_EXIT_OPEN ||
12959               element == EL_EM_EXIT_OPEN ||
12960               element == EL_SP_EXIT_OPEN ||
12961               element == EL_STEEL_EXIT_OPEN ||
12962               element == EL_EM_STEEL_EXIT_OPEN ||
12963               element == EL_SP_TERMINAL ||
12964               element == EL_SP_TERMINAL_ACTIVE ||
12965               element == EL_EXTRA_TIME ||
12966               element == EL_SHIELD_NORMAL ||
12967               element == EL_SHIELD_DEADLY) &&
12968              IS_ANIMATED(graphic))
12969       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12970     else if (IS_MOVING(x, y))
12971       ContinueMoving(x, y);
12972     else if (IS_ACTIVE_BOMB(element))
12973       CheckDynamite(x, y);
12974     else if (element == EL_AMOEBA_GROWING)
12975       AmoebeWaechst(x, y);
12976     else if (element == EL_AMOEBA_SHRINKING)
12977       AmoebaDisappearing(x, y);
12978
12979 #if !USE_NEW_AMOEBA_CODE
12980     else if (IS_AMOEBALIVE(element))
12981       AmoebeAbleger(x, y);
12982 #endif
12983
12984     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12985       Life(x, y);
12986     else if (element == EL_EXIT_CLOSED)
12987       CheckExit(x, y);
12988     else if (element == EL_EM_EXIT_CLOSED)
12989       CheckExitEM(x, y);
12990     else if (element == EL_STEEL_EXIT_CLOSED)
12991       CheckExitSteel(x, y);
12992     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12993       CheckExitSteelEM(x, y);
12994     else if (element == EL_SP_EXIT_CLOSED)
12995       CheckExitSP(x, y);
12996     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12997              element == EL_EXPANDABLE_STEELWALL_GROWING)
12998       MauerWaechst(x, y);
12999     else if (element == EL_EXPANDABLE_WALL ||
13000              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13001              element == EL_EXPANDABLE_WALL_VERTICAL ||
13002              element == EL_EXPANDABLE_WALL_ANY ||
13003              element == EL_BD_EXPANDABLE_WALL)
13004       MauerAbleger(x, y);
13005     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13006              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13007              element == EL_EXPANDABLE_STEELWALL_ANY)
13008       MauerAblegerStahl(x, y);
13009     else if (element == EL_FLAMES)
13010       CheckForDragon(x, y);
13011     else if (element == EL_EXPLOSION)
13012       ; /* drawing of correct explosion animation is handled separately */
13013     else if (element == EL_ELEMENT_SNAPPING ||
13014              element == EL_DIAGONAL_SHRINKING ||
13015              element == EL_DIAGONAL_GROWING)
13016     {
13017       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13018
13019       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13020     }
13021     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13022       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13023
13024 #endif  // ---------------------------------------------------------------------
13025
13026     if (IS_BELT_ACTIVE(element))
13027       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13028
13029     if (game.magic_wall_active)
13030     {
13031       int jx = local_player->jx, jy = local_player->jy;
13032
13033       /* play the element sound at the position nearest to the player */
13034       if ((element == EL_MAGIC_WALL_FULL ||
13035            element == EL_MAGIC_WALL_ACTIVE ||
13036            element == EL_MAGIC_WALL_EMPTYING ||
13037            element == EL_BD_MAGIC_WALL_FULL ||
13038            element == EL_BD_MAGIC_WALL_ACTIVE ||
13039            element == EL_BD_MAGIC_WALL_EMPTYING ||
13040            element == EL_DC_MAGIC_WALL_FULL ||
13041            element == EL_DC_MAGIC_WALL_ACTIVE ||
13042            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13043           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13044       {
13045         magic_wall_x = x;
13046         magic_wall_y = y;
13047       }
13048     }
13049   }
13050
13051 #if 0
13052   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13053 #endif
13054
13055 #if USE_NEW_AMOEBA_CODE
13056   /* new experimental amoeba growth stuff */
13057   if (!(FrameCounter % 8))
13058   {
13059     static unsigned long random = 1684108901;
13060
13061     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13062     {
13063       x = RND(lev_fieldx);
13064       y = RND(lev_fieldy);
13065       element = Feld[x][y];
13066
13067       if (!IS_PLAYER(x,y) &&
13068           (element == EL_EMPTY ||
13069            CAN_GROW_INTO(element) ||
13070            element == EL_QUICKSAND_EMPTY ||
13071            element == EL_QUICKSAND_FAST_EMPTY ||
13072            element == EL_ACID_SPLASH_LEFT ||
13073            element == EL_ACID_SPLASH_RIGHT))
13074       {
13075         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13076             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13077             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13078             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13079           Feld[x][y] = EL_AMOEBA_DROP;
13080       }
13081
13082       random = random * 129 + 1;
13083     }
13084   }
13085 #endif
13086
13087 #if 0
13088   if (game.explosions_delayed)
13089 #endif
13090   {
13091     game.explosions_delayed = FALSE;
13092
13093     SCAN_PLAYFIELD(x, y)
13094     {
13095       element = Feld[x][y];
13096
13097       if (ExplodeField[x][y])
13098         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13099       else if (element == EL_EXPLOSION)
13100         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13101
13102       ExplodeField[x][y] = EX_TYPE_NONE;
13103     }
13104
13105     game.explosions_delayed = TRUE;
13106   }
13107
13108   if (game.magic_wall_active)
13109   {
13110     if (!(game.magic_wall_time_left % 4))
13111     {
13112       int element = Feld[magic_wall_x][magic_wall_y];
13113
13114       if (element == EL_BD_MAGIC_WALL_FULL ||
13115           element == EL_BD_MAGIC_WALL_ACTIVE ||
13116           element == EL_BD_MAGIC_WALL_EMPTYING)
13117         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13118       else if (element == EL_DC_MAGIC_WALL_FULL ||
13119                element == EL_DC_MAGIC_WALL_ACTIVE ||
13120                element == EL_DC_MAGIC_WALL_EMPTYING)
13121         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13122       else
13123         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13124     }
13125
13126     if (game.magic_wall_time_left > 0)
13127     {
13128       game.magic_wall_time_left--;
13129
13130       if (!game.magic_wall_time_left)
13131       {
13132         SCAN_PLAYFIELD(x, y)
13133         {
13134           element = Feld[x][y];
13135
13136           if (element == EL_MAGIC_WALL_ACTIVE ||
13137               element == EL_MAGIC_WALL_FULL)
13138           {
13139             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13140             TEST_DrawLevelField(x, y);
13141           }
13142           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13143                    element == EL_BD_MAGIC_WALL_FULL)
13144           {
13145             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13146             TEST_DrawLevelField(x, y);
13147           }
13148           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13149                    element == EL_DC_MAGIC_WALL_FULL)
13150           {
13151             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13152             TEST_DrawLevelField(x, y);
13153           }
13154         }
13155
13156         game.magic_wall_active = FALSE;
13157       }
13158     }
13159   }
13160
13161   if (game.light_time_left > 0)
13162   {
13163     game.light_time_left--;
13164
13165     if (game.light_time_left == 0)
13166       RedrawAllLightSwitchesAndInvisibleElements();
13167   }
13168
13169   if (game.timegate_time_left > 0)
13170   {
13171     game.timegate_time_left--;
13172
13173     if (game.timegate_time_left == 0)
13174       CloseAllOpenTimegates();
13175   }
13176
13177   if (game.lenses_time_left > 0)
13178   {
13179     game.lenses_time_left--;
13180
13181     if (game.lenses_time_left == 0)
13182       RedrawAllInvisibleElementsForLenses();
13183   }
13184
13185   if (game.magnify_time_left > 0)
13186   {
13187     game.magnify_time_left--;
13188
13189     if (game.magnify_time_left == 0)
13190       RedrawAllInvisibleElementsForMagnifier();
13191   }
13192
13193   for (i = 0; i < MAX_PLAYERS; i++)
13194   {
13195     struct PlayerInfo *player = &stored_player[i];
13196
13197     if (SHIELD_ON(player))
13198     {
13199       if (player->shield_deadly_time_left)
13200         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13201       else if (player->shield_normal_time_left)
13202         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13203     }
13204   }
13205
13206 #if USE_DELAYED_GFX_REDRAW
13207   SCAN_PLAYFIELD(x, y)
13208   {
13209 #if 1
13210     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13211 #else
13212     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13213         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13214 #endif
13215     {
13216       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13217          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13218
13219       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13220         DrawLevelField(x, y);
13221
13222       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13223         DrawLevelFieldCrumbled(x, y);
13224
13225       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13226         DrawLevelFieldCrumbledNeighbours(x, y);
13227
13228       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13229         DrawTwinkleOnField(x, y);
13230     }
13231
13232     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13233   }
13234 #endif
13235
13236   CheckLevelTime();
13237
13238   DrawAllPlayers();
13239   PlayAllPlayersSound();
13240
13241   if (options.debug)                    /* calculate frames per second */
13242   {
13243     static unsigned long fps_counter = 0;
13244     static int fps_frames = 0;
13245     unsigned long fps_delay_ms = Counter() - fps_counter;
13246
13247     fps_frames++;
13248
13249     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13250     {
13251       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13252
13253       fps_frames = 0;
13254       fps_counter = Counter();
13255     }
13256
13257     redraw_mask |= REDRAW_FPS;
13258   }
13259
13260   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13261
13262   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13263   {
13264     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13265
13266     local_player->show_envelope = 0;
13267   }
13268
13269 #if 0
13270   debug_print_timestamp(0, "stop main loop profiling ");
13271   printf("----------------------------------------------------------\n");
13272 #endif
13273
13274   /* use random number generator in every frame to make it less predictable */
13275   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13276     RND(1);
13277 }
13278
13279 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13280 {
13281   int min_x = x, min_y = y, max_x = x, max_y = y;
13282   int i;
13283
13284   for (i = 0; i < MAX_PLAYERS; i++)
13285   {
13286     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13287
13288     if (!stored_player[i].active || &stored_player[i] == player)
13289       continue;
13290
13291     min_x = MIN(min_x, jx);
13292     min_y = MIN(min_y, jy);
13293     max_x = MAX(max_x, jx);
13294     max_y = MAX(max_y, jy);
13295   }
13296
13297   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13298 }
13299
13300 static boolean AllPlayersInVisibleScreen()
13301 {
13302   int i;
13303
13304   for (i = 0; i < MAX_PLAYERS; i++)
13305   {
13306     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13307
13308     if (!stored_player[i].active)
13309       continue;
13310
13311     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13312       return FALSE;
13313   }
13314
13315   return TRUE;
13316 }
13317
13318 void ScrollLevel(int dx, int dy)
13319 {
13320 #if 0
13321   /* (directly solved in BlitBitmap() now) */
13322   static Bitmap *bitmap_db_field2 = NULL;
13323   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13324   int x, y;
13325 #else
13326   int x, y;
13327 #endif
13328
13329 #if 0
13330   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13331   /* only horizontal XOR vertical scroll direction allowed */
13332   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13333     return;
13334 #endif
13335
13336 #if 0
13337   /* (directly solved in BlitBitmap() now) */
13338   if (bitmap_db_field2 == NULL)
13339     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13340
13341   /* needed when blitting directly to same bitmap -- should not be needed with
13342      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13343   BlitBitmap(drawto_field, bitmap_db_field2,
13344              FX + TILEX * (dx == -1) - softscroll_offset,
13345              FY + TILEY * (dy == -1) - softscroll_offset,
13346              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13347              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13348              FX + TILEX * (dx == 1) - softscroll_offset,
13349              FY + TILEY * (dy == 1) - softscroll_offset);
13350   BlitBitmap(bitmap_db_field2, drawto_field,
13351              FX + TILEX * (dx == 1) - softscroll_offset,
13352              FY + TILEY * (dy == 1) - softscroll_offset,
13353              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355              FX + TILEX * (dx == 1) - softscroll_offset,
13356              FY + TILEY * (dy == 1) - softscroll_offset);
13357
13358 #else
13359
13360 #if 0
13361   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13362   int xsize = (BX2 - BX1 + 1);
13363   int ysize = (BY2 - BY1 + 1);
13364   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13365   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13366   int step  = (start < end ? +1 : -1);
13367
13368   for (i = start; i != end; i += step)
13369   {
13370     BlitBitmap(drawto_field, drawto_field,
13371                FX + TILEX * (dx != 0 ? i + step : 0),
13372                FY + TILEY * (dy != 0 ? i + step : 0),
13373                TILEX * (dx != 0 ? 1 : xsize),
13374                TILEY * (dy != 0 ? 1 : ysize),
13375                FX + TILEX * (dx != 0 ? i : 0),
13376                FY + TILEY * (dy != 0 ? i : 0));
13377   }
13378
13379 #else
13380
13381 #if NEW_TILESIZE
13382 #if NEW_SCROLL
13383   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13384 #else
13385   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13386 #endif
13387 #else
13388 #if NEW_SCROLL
13389   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13390 #else
13391   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13392 #endif
13393 #endif
13394
13395 #if NEW_TILESIZE
13396   BlitBitmap(drawto_field, drawto_field,
13397              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13398              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13399              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13400              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13401              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13402              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13403 #else
13404   BlitBitmap(drawto_field, drawto_field,
13405              FX + TILEX * (dx == -1) - softscroll_offset,
13406              FY + TILEY * (dy == -1) - softscroll_offset,
13407              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13408              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13409              FX + TILEX * (dx == 1) - softscroll_offset,
13410              FY + TILEY * (dy == 1) - softscroll_offset);
13411 #endif
13412
13413 #endif
13414 #endif
13415
13416   if (dx != 0)
13417   {
13418     x = (dx == 1 ? BX1 : BX2);
13419     for (y = BY1; y <= BY2; y++)
13420       DrawScreenField(x, y);
13421   }
13422
13423   if (dy != 0)
13424   {
13425     y = (dy == 1 ? BY1 : BY2);
13426     for (x = BX1; x <= BX2; x++)
13427       DrawScreenField(x, y);
13428   }
13429
13430   redraw_mask |= REDRAW_FIELD;
13431 }
13432
13433 static boolean canFallDown(struct PlayerInfo *player)
13434 {
13435   int jx = player->jx, jy = player->jy;
13436
13437   return (IN_LEV_FIELD(jx, jy + 1) &&
13438           (IS_FREE(jx, jy + 1) ||
13439            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13440           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13441           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13442 }
13443
13444 static boolean canPassField(int x, int y, int move_dir)
13445 {
13446   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13447   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13448   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13449   int nextx = x + dx;
13450   int nexty = y + dy;
13451   int element = Feld[x][y];
13452
13453   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13454           !CAN_MOVE(element) &&
13455           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13456           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13457           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13458 }
13459
13460 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13461 {
13462   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13463   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13464   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13465   int newx = x + dx;
13466   int newy = y + dy;
13467
13468   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13469           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13470           (IS_DIGGABLE(Feld[newx][newy]) ||
13471            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13472            canPassField(newx, newy, move_dir)));
13473 }
13474
13475 static void CheckGravityMovement(struct PlayerInfo *player)
13476 {
13477 #if USE_PLAYER_GRAVITY
13478   if (player->gravity && !player->programmed_action)
13479 #else
13480   if (game.gravity && !player->programmed_action)
13481 #endif
13482   {
13483     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13484     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13485     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13486     int jx = player->jx, jy = player->jy;
13487     boolean player_is_moving_to_valid_field =
13488       (!player_is_snapping &&
13489        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13490         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13491     boolean player_can_fall_down = canFallDown(player);
13492
13493     if (player_can_fall_down &&
13494         !player_is_moving_to_valid_field)
13495       player->programmed_action = MV_DOWN;
13496   }
13497 }
13498
13499 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13500 {
13501   return CheckGravityMovement(player);
13502
13503 #if USE_PLAYER_GRAVITY
13504   if (player->gravity && !player->programmed_action)
13505 #else
13506   if (game.gravity && !player->programmed_action)
13507 #endif
13508   {
13509     int jx = player->jx, jy = player->jy;
13510     boolean field_under_player_is_free =
13511       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13512     boolean player_is_standing_on_valid_field =
13513       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13514        (IS_WALKABLE(Feld[jx][jy]) &&
13515         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13516
13517     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13518       player->programmed_action = MV_DOWN;
13519   }
13520 }
13521
13522 /*
13523   MovePlayerOneStep()
13524   -----------------------------------------------------------------------------
13525   dx, dy:               direction (non-diagonal) to try to move the player to
13526   real_dx, real_dy:     direction as read from input device (can be diagonal)
13527 */
13528
13529 boolean MovePlayerOneStep(struct PlayerInfo *player,
13530                           int dx, int dy, int real_dx, int real_dy)
13531 {
13532   int jx = player->jx, jy = player->jy;
13533   int new_jx = jx + dx, new_jy = jy + dy;
13534 #if !USE_FIXED_DONT_RUN_INTO
13535   int element;
13536 #endif
13537   int can_move;
13538   boolean player_can_move = !player->cannot_move;
13539
13540   if (!player->active || (!dx && !dy))
13541     return MP_NO_ACTION;
13542
13543   player->MovDir = (dx < 0 ? MV_LEFT :
13544                     dx > 0 ? MV_RIGHT :
13545                     dy < 0 ? MV_UP :
13546                     dy > 0 ? MV_DOWN :  MV_NONE);
13547
13548   if (!IN_LEV_FIELD(new_jx, new_jy))
13549     return MP_NO_ACTION;
13550
13551   if (!player_can_move)
13552   {
13553     if (player->MovPos == 0)
13554     {
13555       player->is_moving = FALSE;
13556       player->is_digging = FALSE;
13557       player->is_collecting = FALSE;
13558       player->is_snapping = FALSE;
13559       player->is_pushing = FALSE;
13560     }
13561   }
13562
13563 #if 1
13564   if (!options.network && game.centered_player_nr == -1 &&
13565       !AllPlayersInSight(player, new_jx, new_jy))
13566     return MP_NO_ACTION;
13567 #else
13568   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13569     return MP_NO_ACTION;
13570 #endif
13571
13572 #if !USE_FIXED_DONT_RUN_INTO
13573   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13574
13575   /* (moved to DigField()) */
13576   if (player_can_move && DONT_RUN_INTO(element))
13577   {
13578     if (element == EL_ACID && dx == 0 && dy == 1)
13579     {
13580       SplashAcid(new_jx, new_jy);
13581       Feld[jx][jy] = EL_PLAYER_1;
13582       InitMovingField(jx, jy, MV_DOWN);
13583       Store[jx][jy] = EL_ACID;
13584       ContinueMoving(jx, jy);
13585       BuryPlayer(player);
13586     }
13587     else
13588       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13589
13590     return MP_MOVING;
13591   }
13592 #endif
13593
13594   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13595   if (can_move != MP_MOVING)
13596     return can_move;
13597
13598   /* check if DigField() has caused relocation of the player */
13599   if (player->jx != jx || player->jy != jy)
13600     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13601
13602   StorePlayer[jx][jy] = 0;
13603   player->last_jx = jx;
13604   player->last_jy = jy;
13605   player->jx = new_jx;
13606   player->jy = new_jy;
13607   StorePlayer[new_jx][new_jy] = player->element_nr;
13608
13609   if (player->move_delay_value_next != -1)
13610   {
13611     player->move_delay_value = player->move_delay_value_next;
13612     player->move_delay_value_next = -1;
13613   }
13614
13615   player->MovPos =
13616     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13617
13618   player->step_counter++;
13619
13620   PlayerVisit[jx][jy] = FrameCounter;
13621
13622 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13623   player->is_moving = TRUE;
13624 #endif
13625
13626 #if 1
13627   /* should better be called in MovePlayer(), but this breaks some tapes */
13628   ScrollPlayer(player, SCROLL_INIT);
13629 #endif
13630
13631   return MP_MOVING;
13632 }
13633
13634 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13635 {
13636   int jx = player->jx, jy = player->jy;
13637   int old_jx = jx, old_jy = jy;
13638   int moved = MP_NO_ACTION;
13639
13640   if (!player->active)
13641     return FALSE;
13642
13643   if (!dx && !dy)
13644   {
13645     if (player->MovPos == 0)
13646     {
13647       player->is_moving = FALSE;
13648       player->is_digging = FALSE;
13649       player->is_collecting = FALSE;
13650       player->is_snapping = FALSE;
13651       player->is_pushing = FALSE;
13652     }
13653
13654     return FALSE;
13655   }
13656
13657   if (player->move_delay > 0)
13658     return FALSE;
13659
13660   player->move_delay = -1;              /* set to "uninitialized" value */
13661
13662   /* store if player is automatically moved to next field */
13663   player->is_auto_moving = (player->programmed_action != MV_NONE);
13664
13665   /* remove the last programmed player action */
13666   player->programmed_action = 0;
13667
13668   if (player->MovPos)
13669   {
13670     /* should only happen if pre-1.2 tape recordings are played */
13671     /* this is only for backward compatibility */
13672
13673     int original_move_delay_value = player->move_delay_value;
13674
13675 #if DEBUG
13676     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13677            tape.counter);
13678 #endif
13679
13680     /* scroll remaining steps with finest movement resolution */
13681     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13682
13683     while (player->MovPos)
13684     {
13685       ScrollPlayer(player, SCROLL_GO_ON);
13686       ScrollScreen(NULL, SCROLL_GO_ON);
13687
13688       AdvanceFrameAndPlayerCounters(player->index_nr);
13689
13690       DrawAllPlayers();
13691       BackToFront();
13692     }
13693
13694     player->move_delay_value = original_move_delay_value;
13695   }
13696
13697   player->is_active = FALSE;
13698
13699   if (player->last_move_dir & MV_HORIZONTAL)
13700   {
13701     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13702       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13703   }
13704   else
13705   {
13706     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13707       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13708   }
13709
13710 #if USE_FIXED_BORDER_RUNNING_GFX
13711   if (!moved && !player->is_active)
13712   {
13713     player->is_moving = FALSE;
13714     player->is_digging = FALSE;
13715     player->is_collecting = FALSE;
13716     player->is_snapping = FALSE;
13717     player->is_pushing = FALSE;
13718   }
13719 #endif
13720
13721   jx = player->jx;
13722   jy = player->jy;
13723
13724 #if 1
13725   if (moved & MP_MOVING && !ScreenMovPos &&
13726       (player->index_nr == game.centered_player_nr ||
13727        game.centered_player_nr == -1))
13728 #else
13729   if (moved & MP_MOVING && !ScreenMovPos &&
13730       (player == local_player || !options.network))
13731 #endif
13732   {
13733     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13734     int offset = game.scroll_delay_value;
13735
13736     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13737     {
13738       /* actual player has left the screen -- scroll in that direction */
13739       if (jx != old_jx)         /* player has moved horizontally */
13740         scroll_x += (jx - old_jx);
13741       else                      /* player has moved vertically */
13742         scroll_y += (jy - old_jy);
13743     }
13744     else
13745     {
13746       if (jx != old_jx)         /* player has moved horizontally */
13747       {
13748         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13749             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13750           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13751
13752         /* don't scroll over playfield boundaries */
13753         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13754           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13755
13756         /* don't scroll more than one field at a time */
13757         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13758
13759         /* don't scroll against the player's moving direction */
13760         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13761             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13762           scroll_x = old_scroll_x;
13763       }
13764       else                      /* player has moved vertically */
13765       {
13766         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13767             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13768           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13769
13770         /* don't scroll over playfield boundaries */
13771         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13772           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13773
13774         /* don't scroll more than one field at a time */
13775         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13776
13777         /* don't scroll against the player's moving direction */
13778         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13779             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13780           scroll_y = old_scroll_y;
13781       }
13782     }
13783
13784     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13785     {
13786 #if 1
13787       if (!options.network && game.centered_player_nr == -1 &&
13788           !AllPlayersInVisibleScreen())
13789       {
13790         scroll_x = old_scroll_x;
13791         scroll_y = old_scroll_y;
13792       }
13793       else
13794 #else
13795       if (!options.network && !AllPlayersInVisibleScreen())
13796       {
13797         scroll_x = old_scroll_x;
13798         scroll_y = old_scroll_y;
13799       }
13800       else
13801 #endif
13802       {
13803         ScrollScreen(player, SCROLL_INIT);
13804         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13805       }
13806     }
13807   }
13808
13809   player->StepFrame = 0;
13810
13811   if (moved & MP_MOVING)
13812   {
13813     if (old_jx != jx && old_jy == jy)
13814       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13815     else if (old_jx == jx && old_jy != jy)
13816       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13817
13818     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13819
13820     player->last_move_dir = player->MovDir;
13821     player->is_moving = TRUE;
13822     player->is_snapping = FALSE;
13823     player->is_switching = FALSE;
13824     player->is_dropping = FALSE;
13825     player->is_dropping_pressed = FALSE;
13826     player->drop_pressed_delay = 0;
13827
13828 #if 0
13829     /* should better be called here than above, but this breaks some tapes */
13830     ScrollPlayer(player, SCROLL_INIT);
13831 #endif
13832   }
13833   else
13834   {
13835     CheckGravityMovementWhenNotMoving(player);
13836
13837     player->is_moving = FALSE;
13838
13839     /* at this point, the player is allowed to move, but cannot move right now
13840        (e.g. because of something blocking the way) -- ensure that the player
13841        is also allowed to move in the next frame (in old versions before 3.1.1,
13842        the player was forced to wait again for eight frames before next try) */
13843
13844     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13845       player->move_delay = 0;   /* allow direct movement in the next frame */
13846   }
13847
13848   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13849     player->move_delay = player->move_delay_value;
13850
13851   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13852   {
13853     TestIfPlayerTouchesBadThing(jx, jy);
13854     TestIfPlayerTouchesCustomElement(jx, jy);
13855   }
13856
13857   if (!player->active)
13858     RemovePlayer(player);
13859
13860   return moved;
13861 }
13862
13863 void ScrollPlayer(struct PlayerInfo *player, int mode)
13864 {
13865   int jx = player->jx, jy = player->jy;
13866   int last_jx = player->last_jx, last_jy = player->last_jy;
13867   int move_stepsize = TILEX / player->move_delay_value;
13868
13869 #if USE_NEW_PLAYER_SPEED
13870   if (!player->active)
13871     return;
13872
13873   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13874     return;
13875 #else
13876   if (!player->active || player->MovPos == 0)
13877     return;
13878 #endif
13879
13880   if (mode == SCROLL_INIT)
13881   {
13882     player->actual_frame_counter = FrameCounter;
13883     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13884
13885     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13886         Feld[last_jx][last_jy] == EL_EMPTY)
13887     {
13888       int last_field_block_delay = 0;   /* start with no blocking at all */
13889       int block_delay_adjustment = player->block_delay_adjustment;
13890
13891       /* if player blocks last field, add delay for exactly one move */
13892       if (player->block_last_field)
13893       {
13894         last_field_block_delay += player->move_delay_value;
13895
13896         /* when blocking enabled, prevent moving up despite gravity */
13897 #if USE_PLAYER_GRAVITY
13898         if (player->gravity && player->MovDir == MV_UP)
13899           block_delay_adjustment = -1;
13900 #else
13901         if (game.gravity && player->MovDir == MV_UP)
13902           block_delay_adjustment = -1;
13903 #endif
13904       }
13905
13906       /* add block delay adjustment (also possible when not blocking) */
13907       last_field_block_delay += block_delay_adjustment;
13908
13909       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13910       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13911     }
13912
13913 #if USE_NEW_PLAYER_SPEED
13914     if (player->MovPos != 0)    /* player has not yet reached destination */
13915       return;
13916 #else
13917     return;
13918 #endif
13919   }
13920   else if (!FrameReached(&player->actual_frame_counter, 1))
13921     return;
13922
13923 #if USE_NEW_PLAYER_SPEED
13924   if (player->MovPos != 0)
13925   {
13926     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13927     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13928
13929     /* before DrawPlayer() to draw correct player graphic for this case */
13930     if (player->MovPos == 0)
13931       CheckGravityMovement(player);
13932   }
13933 #else
13934   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13935   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13936
13937   /* before DrawPlayer() to draw correct player graphic for this case */
13938   if (player->MovPos == 0)
13939     CheckGravityMovement(player);
13940 #endif
13941
13942   if (player->MovPos == 0)      /* player reached destination field */
13943   {
13944     if (player->move_delay_reset_counter > 0)
13945     {
13946       player->move_delay_reset_counter--;
13947
13948       if (player->move_delay_reset_counter == 0)
13949       {
13950         /* continue with normal speed after quickly moving through gate */
13951         HALVE_PLAYER_SPEED(player);
13952
13953         /* be able to make the next move without delay */
13954         player->move_delay = 0;
13955       }
13956     }
13957
13958     player->last_jx = jx;
13959     player->last_jy = jy;
13960
13961     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13962         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13963 #if 1
13964         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13965 #endif
13966         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13967         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13968 #if 1
13969         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13970 #endif
13971         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13972         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13973     {
13974       DrawPlayer(player);       /* needed here only to cleanup last field */
13975       RemovePlayer(player);
13976
13977       if (local_player->friends_still_needed == 0 ||
13978           IS_SP_ELEMENT(Feld[jx][jy]))
13979         PlayerWins(player);
13980     }
13981
13982     /* this breaks one level: "machine", level 000 */
13983     {
13984       int move_direction = player->MovDir;
13985       int enter_side = MV_DIR_OPPOSITE(move_direction);
13986       int leave_side = move_direction;
13987       int old_jx = last_jx;
13988       int old_jy = last_jy;
13989       int old_element = Feld[old_jx][old_jy];
13990       int new_element = Feld[jx][jy];
13991
13992       if (IS_CUSTOM_ELEMENT(old_element))
13993         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13994                                    CE_LEFT_BY_PLAYER,
13995                                    player->index_bit, leave_side);
13996
13997       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13998                                           CE_PLAYER_LEAVES_X,
13999                                           player->index_bit, leave_side);
14000
14001       if (IS_CUSTOM_ELEMENT(new_element))
14002         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14003                                    player->index_bit, enter_side);
14004
14005       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14006                                           CE_PLAYER_ENTERS_X,
14007                                           player->index_bit, enter_side);
14008
14009 #if USE_FIX_CE_ACTION_WITH_PLAYER
14010       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14011                                         CE_MOVE_OF_X, move_direction);
14012 #else
14013       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14014                                         CE_MOVE_OF_X, move_direction);
14015 #endif
14016     }
14017
14018     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14019     {
14020       TestIfPlayerTouchesBadThing(jx, jy);
14021       TestIfPlayerTouchesCustomElement(jx, jy);
14022
14023       /* needed because pushed element has not yet reached its destination,
14024          so it would trigger a change event at its previous field location */
14025       if (!player->is_pushing)
14026         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14027
14028       if (!player->active)
14029         RemovePlayer(player);
14030     }
14031
14032     if (!local_player->LevelSolved && level.use_step_counter)
14033     {
14034       int i;
14035
14036       TimePlayed++;
14037
14038       if (TimeLeft > 0)
14039       {
14040         TimeLeft--;
14041
14042         if (TimeLeft <= 10 && setup.time_limit)
14043           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14044
14045 #if 1
14046         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14047
14048         DisplayGameControlValues();
14049 #else
14050         DrawGameValue_Time(TimeLeft);
14051 #endif
14052
14053         if (!TimeLeft && setup.time_limit)
14054           for (i = 0; i < MAX_PLAYERS; i++)
14055             KillPlayer(&stored_player[i]);
14056       }
14057 #if 1
14058       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14059       {
14060         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14061
14062         DisplayGameControlValues();
14063       }
14064 #else
14065       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14066         DrawGameValue_Time(TimePlayed);
14067 #endif
14068     }
14069
14070     if (tape.single_step && tape.recording && !tape.pausing &&
14071         !player->programmed_action)
14072       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14073   }
14074 }
14075
14076 void ScrollScreen(struct PlayerInfo *player, int mode)
14077 {
14078   static unsigned long screen_frame_counter = 0;
14079
14080   if (mode == SCROLL_INIT)
14081   {
14082     /* set scrolling step size according to actual player's moving speed */
14083     ScrollStepSize = TILEX / player->move_delay_value;
14084
14085     screen_frame_counter = FrameCounter;
14086     ScreenMovDir = player->MovDir;
14087     ScreenMovPos = player->MovPos;
14088     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14089     return;
14090   }
14091   else if (!FrameReached(&screen_frame_counter, 1))
14092     return;
14093
14094   if (ScreenMovPos)
14095   {
14096     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14097     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14098     redraw_mask |= REDRAW_FIELD;
14099   }
14100   else
14101     ScreenMovDir = MV_NONE;
14102 }
14103
14104 void TestIfPlayerTouchesCustomElement(int x, int y)
14105 {
14106   static int xy[4][2] =
14107   {
14108     { 0, -1 },
14109     { -1, 0 },
14110     { +1, 0 },
14111     { 0, +1 }
14112   };
14113   static int trigger_sides[4][2] =
14114   {
14115     /* center side       border side */
14116     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14117     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14118     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14119     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14120   };
14121   static int touch_dir[4] =
14122   {
14123     MV_LEFT | MV_RIGHT,
14124     MV_UP   | MV_DOWN,
14125     MV_UP   | MV_DOWN,
14126     MV_LEFT | MV_RIGHT
14127   };
14128   int center_element = Feld[x][y];      /* should always be non-moving! */
14129   int i;
14130
14131   for (i = 0; i < NUM_DIRECTIONS; i++)
14132   {
14133     int xx = x + xy[i][0];
14134     int yy = y + xy[i][1];
14135     int center_side = trigger_sides[i][0];
14136     int border_side = trigger_sides[i][1];
14137     int border_element;
14138
14139     if (!IN_LEV_FIELD(xx, yy))
14140       continue;
14141
14142     if (IS_PLAYER(x, y))                /* player found at center element */
14143     {
14144       struct PlayerInfo *player = PLAYERINFO(x, y);
14145
14146       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14147         border_element = Feld[xx][yy];          /* may be moving! */
14148       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14149         border_element = Feld[xx][yy];
14150       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14151         border_element = MovingOrBlocked2Element(xx, yy);
14152       else
14153         continue;               /* center and border element do not touch */
14154
14155       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14156                                  player->index_bit, border_side);
14157       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14158                                           CE_PLAYER_TOUCHES_X,
14159                                           player->index_bit, border_side);
14160
14161 #if USE_FIX_CE_ACTION_WITH_PLAYER
14162       {
14163         /* use player element that is initially defined in the level playfield,
14164            not the player element that corresponds to the runtime player number
14165            (example: a level that contains EL_PLAYER_3 as the only player would
14166            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14167         int player_element = PLAYERINFO(x, y)->initial_element;
14168
14169         CheckElementChangeBySide(xx, yy, border_element, player_element,
14170                                  CE_TOUCHING_X, border_side);
14171       }
14172 #endif
14173     }
14174     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14175     {
14176       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14177
14178       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14179       {
14180         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14181           continue;             /* center and border element do not touch */
14182       }
14183
14184       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14185                                  player->index_bit, center_side);
14186       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14187                                           CE_PLAYER_TOUCHES_X,
14188                                           player->index_bit, center_side);
14189
14190 #if USE_FIX_CE_ACTION_WITH_PLAYER
14191       {
14192         /* use player element that is initially defined in the level playfield,
14193            not the player element that corresponds to the runtime player number
14194            (example: a level that contains EL_PLAYER_3 as the only player would
14195            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14196         int player_element = PLAYERINFO(xx, yy)->initial_element;
14197
14198         CheckElementChangeBySide(x, y, center_element, player_element,
14199                                  CE_TOUCHING_X, center_side);
14200       }
14201 #endif
14202
14203       break;
14204     }
14205   }
14206 }
14207
14208 #if USE_ELEMENT_TOUCHING_BUGFIX
14209
14210 void TestIfElementTouchesCustomElement(int x, int y)
14211 {
14212   static int xy[4][2] =
14213   {
14214     { 0, -1 },
14215     { -1, 0 },
14216     { +1, 0 },
14217     { 0, +1 }
14218   };
14219   static int trigger_sides[4][2] =
14220   {
14221     /* center side      border side */
14222     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14223     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14224     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14225     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14226   };
14227   static int touch_dir[4] =
14228   {
14229     MV_LEFT | MV_RIGHT,
14230     MV_UP   | MV_DOWN,
14231     MV_UP   | MV_DOWN,
14232     MV_LEFT | MV_RIGHT
14233   };
14234   boolean change_center_element = FALSE;
14235   int center_element = Feld[x][y];      /* should always be non-moving! */
14236   int border_element_old[NUM_DIRECTIONS];
14237   int i;
14238
14239   for (i = 0; i < NUM_DIRECTIONS; i++)
14240   {
14241     int xx = x + xy[i][0];
14242     int yy = y + xy[i][1];
14243     int border_element;
14244
14245     border_element_old[i] = -1;
14246
14247     if (!IN_LEV_FIELD(xx, yy))
14248       continue;
14249
14250     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14251       border_element = Feld[xx][yy];    /* may be moving! */
14252     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14253       border_element = Feld[xx][yy];
14254     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14255       border_element = MovingOrBlocked2Element(xx, yy);
14256     else
14257       continue;                 /* center and border element do not touch */
14258
14259     border_element_old[i] = border_element;
14260   }
14261
14262   for (i = 0; i < NUM_DIRECTIONS; i++)
14263   {
14264     int xx = x + xy[i][0];
14265     int yy = y + xy[i][1];
14266     int center_side = trigger_sides[i][0];
14267     int border_element = border_element_old[i];
14268
14269     if (border_element == -1)
14270       continue;
14271
14272     /* check for change of border element */
14273     CheckElementChangeBySide(xx, yy, border_element, center_element,
14274                              CE_TOUCHING_X, center_side);
14275
14276     /* (center element cannot be player, so we dont have to check this here) */
14277   }
14278
14279   for (i = 0; i < NUM_DIRECTIONS; i++)
14280   {
14281     int xx = x + xy[i][0];
14282     int yy = y + xy[i][1];
14283     int border_side = trigger_sides[i][1];
14284     int border_element = border_element_old[i];
14285
14286     if (border_element == -1)
14287       continue;
14288
14289     /* check for change of center element (but change it only once) */
14290     if (!change_center_element)
14291       change_center_element =
14292         CheckElementChangeBySide(x, y, center_element, border_element,
14293                                  CE_TOUCHING_X, border_side);
14294
14295 #if USE_FIX_CE_ACTION_WITH_PLAYER
14296     if (IS_PLAYER(xx, yy))
14297     {
14298       /* use player element that is initially defined in the level playfield,
14299          not the player element that corresponds to the runtime player number
14300          (example: a level that contains EL_PLAYER_3 as the only player would
14301          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14302       int player_element = PLAYERINFO(xx, yy)->initial_element;
14303
14304       CheckElementChangeBySide(x, y, center_element, player_element,
14305                                CE_TOUCHING_X, border_side);
14306     }
14307 #endif
14308   }
14309 }
14310
14311 #else
14312
14313 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14314 {
14315   static int xy[4][2] =
14316   {
14317     { 0, -1 },
14318     { -1, 0 },
14319     { +1, 0 },
14320     { 0, +1 }
14321   };
14322   static int trigger_sides[4][2] =
14323   {
14324     /* center side      border side */
14325     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14326     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14327     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14328     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14329   };
14330   static int touch_dir[4] =
14331   {
14332     MV_LEFT | MV_RIGHT,
14333     MV_UP   | MV_DOWN,
14334     MV_UP   | MV_DOWN,
14335     MV_LEFT | MV_RIGHT
14336   };
14337   boolean change_center_element = FALSE;
14338   int center_element = Feld[x][y];      /* should always be non-moving! */
14339   int i;
14340
14341   for (i = 0; i < NUM_DIRECTIONS; i++)
14342   {
14343     int xx = x + xy[i][0];
14344     int yy = y + xy[i][1];
14345     int center_side = trigger_sides[i][0];
14346     int border_side = trigger_sides[i][1];
14347     int border_element;
14348
14349     if (!IN_LEV_FIELD(xx, yy))
14350       continue;
14351
14352     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14353       border_element = Feld[xx][yy];    /* may be moving! */
14354     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14355       border_element = Feld[xx][yy];
14356     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14357       border_element = MovingOrBlocked2Element(xx, yy);
14358     else
14359       continue;                 /* center and border element do not touch */
14360
14361     /* check for change of center element (but change it only once) */
14362     if (!change_center_element)
14363       change_center_element =
14364         CheckElementChangeBySide(x, y, center_element, border_element,
14365                                  CE_TOUCHING_X, border_side);
14366
14367     /* check for change of border element */
14368     CheckElementChangeBySide(xx, yy, border_element, center_element,
14369                              CE_TOUCHING_X, center_side);
14370   }
14371 }
14372
14373 #endif
14374
14375 void TestIfElementHitsCustomElement(int x, int y, int direction)
14376 {
14377   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14378   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14379   int hitx = x + dx, hity = y + dy;
14380   int hitting_element = Feld[x][y];
14381   int touched_element;
14382
14383   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14384     return;
14385
14386   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14387                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14388
14389   if (IN_LEV_FIELD(hitx, hity))
14390   {
14391     int opposite_direction = MV_DIR_OPPOSITE(direction);
14392     int hitting_side = direction;
14393     int touched_side = opposite_direction;
14394     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14395                           MovDir[hitx][hity] != direction ||
14396                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14397
14398     object_hit = TRUE;
14399
14400     if (object_hit)
14401     {
14402       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14403                                CE_HITTING_X, touched_side);
14404
14405       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14406                                CE_HIT_BY_X, hitting_side);
14407
14408       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14409                                CE_HIT_BY_SOMETHING, opposite_direction);
14410
14411 #if USE_FIX_CE_ACTION_WITH_PLAYER
14412       if (IS_PLAYER(hitx, hity))
14413       {
14414         /* use player element that is initially defined in the level playfield,
14415            not the player element that corresponds to the runtime player number
14416            (example: a level that contains EL_PLAYER_3 as the only player would
14417            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14418         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14419
14420         CheckElementChangeBySide(x, y, hitting_element, player_element,
14421                                  CE_HITTING_X, touched_side);
14422       }
14423 #endif
14424     }
14425   }
14426
14427   /* "hitting something" is also true when hitting the playfield border */
14428   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14429                            CE_HITTING_SOMETHING, direction);
14430 }
14431
14432 #if 0
14433 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14434 {
14435   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14436   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14437   int hitx = x + dx, hity = y + dy;
14438   int hitting_element = Feld[x][y];
14439   int touched_element;
14440 #if 0
14441   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14442                         !IS_FREE(hitx, hity) &&
14443                         (!IS_MOVING(hitx, hity) ||
14444                          MovDir[hitx][hity] != direction ||
14445                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14446 #endif
14447
14448   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14449     return;
14450
14451 #if 0
14452   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14453     return;
14454 #endif
14455
14456   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14457                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14458
14459   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14460                            EP_CAN_SMASH_EVERYTHING, direction);
14461
14462   if (IN_LEV_FIELD(hitx, hity))
14463   {
14464     int opposite_direction = MV_DIR_OPPOSITE(direction);
14465     int hitting_side = direction;
14466     int touched_side = opposite_direction;
14467 #if 0
14468     int touched_element = MovingOrBlocked2Element(hitx, hity);
14469 #endif
14470 #if 1
14471     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14472                           MovDir[hitx][hity] != direction ||
14473                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14474
14475     object_hit = TRUE;
14476 #endif
14477
14478     if (object_hit)
14479     {
14480       int i;
14481
14482       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14483                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14484
14485       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14486                                CE_OTHER_IS_SMASHING, touched_side);
14487
14488       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14489                                CE_OTHER_GETS_SMASHED, hitting_side);
14490     }
14491   }
14492 }
14493 #endif
14494
14495 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14496 {
14497   int i, kill_x = -1, kill_y = -1;
14498
14499   int bad_element = -1;
14500   static int test_xy[4][2] =
14501   {
14502     { 0, -1 },
14503     { -1, 0 },
14504     { +1, 0 },
14505     { 0, +1 }
14506   };
14507   static int test_dir[4] =
14508   {
14509     MV_UP,
14510     MV_LEFT,
14511     MV_RIGHT,
14512     MV_DOWN
14513   };
14514
14515   for (i = 0; i < NUM_DIRECTIONS; i++)
14516   {
14517     int test_x, test_y, test_move_dir, test_element;
14518
14519     test_x = good_x + test_xy[i][0];
14520     test_y = good_y + test_xy[i][1];
14521
14522     if (!IN_LEV_FIELD(test_x, test_y))
14523       continue;
14524
14525     test_move_dir =
14526       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14527
14528     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14529
14530     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14531        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14532     */
14533     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14534         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14535     {
14536       kill_x = test_x;
14537       kill_y = test_y;
14538       bad_element = test_element;
14539
14540       break;
14541     }
14542   }
14543
14544   if (kill_x != -1 || kill_y != -1)
14545   {
14546     if (IS_PLAYER(good_x, good_y))
14547     {
14548       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14549
14550       if (player->shield_deadly_time_left > 0 &&
14551           !IS_INDESTRUCTIBLE(bad_element))
14552         Bang(kill_x, kill_y);
14553       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14554         KillPlayer(player);
14555     }
14556     else
14557       Bang(good_x, good_y);
14558   }
14559 }
14560
14561 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14562 {
14563   int i, kill_x = -1, kill_y = -1;
14564   int bad_element = Feld[bad_x][bad_y];
14565   static int test_xy[4][2] =
14566   {
14567     { 0, -1 },
14568     { -1, 0 },
14569     { +1, 0 },
14570     { 0, +1 }
14571   };
14572   static int touch_dir[4] =
14573   {
14574     MV_LEFT | MV_RIGHT,
14575     MV_UP   | MV_DOWN,
14576     MV_UP   | MV_DOWN,
14577     MV_LEFT | MV_RIGHT
14578   };
14579   static int test_dir[4] =
14580   {
14581     MV_UP,
14582     MV_LEFT,
14583     MV_RIGHT,
14584     MV_DOWN
14585   };
14586
14587   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14588     return;
14589
14590   for (i = 0; i < NUM_DIRECTIONS; i++)
14591   {
14592     int test_x, test_y, test_move_dir, test_element;
14593
14594     test_x = bad_x + test_xy[i][0];
14595     test_y = bad_y + test_xy[i][1];
14596
14597     if (!IN_LEV_FIELD(test_x, test_y))
14598       continue;
14599
14600     test_move_dir =
14601       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14602
14603     test_element = Feld[test_x][test_y];
14604
14605     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14606        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14607     */
14608     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14609         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14610     {
14611       /* good thing is player or penguin that does not move away */
14612       if (IS_PLAYER(test_x, test_y))
14613       {
14614         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14615
14616         if (bad_element == EL_ROBOT && player->is_moving)
14617           continue;     /* robot does not kill player if he is moving */
14618
14619         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14620         {
14621           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14622             continue;           /* center and border element do not touch */
14623         }
14624
14625         kill_x = test_x;
14626         kill_y = test_y;
14627
14628         break;
14629       }
14630       else if (test_element == EL_PENGUIN)
14631       {
14632         kill_x = test_x;
14633         kill_y = test_y;
14634
14635         break;
14636       }
14637     }
14638   }
14639
14640   if (kill_x != -1 || kill_y != -1)
14641   {
14642     if (IS_PLAYER(kill_x, kill_y))
14643     {
14644       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14645
14646       if (player->shield_deadly_time_left > 0 &&
14647           !IS_INDESTRUCTIBLE(bad_element))
14648         Bang(bad_x, bad_y);
14649       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14650         KillPlayer(player);
14651     }
14652     else
14653       Bang(kill_x, kill_y);
14654   }
14655 }
14656
14657 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14658 {
14659   int bad_element = Feld[bad_x][bad_y];
14660   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14661   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14662   int test_x = bad_x + dx, test_y = bad_y + dy;
14663   int test_move_dir, test_element;
14664   int kill_x = -1, kill_y = -1;
14665
14666   if (!IN_LEV_FIELD(test_x, test_y))
14667     return;
14668
14669   test_move_dir =
14670     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14671
14672   test_element = Feld[test_x][test_y];
14673
14674   if (test_move_dir != bad_move_dir)
14675   {
14676     /* good thing can be player or penguin that does not move away */
14677     if (IS_PLAYER(test_x, test_y))
14678     {
14679       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14680
14681       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14682          player as being hit when he is moving towards the bad thing, because
14683          the "get hit by" condition would be lost after the player stops) */
14684       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14685         return;         /* player moves away from bad thing */
14686
14687       kill_x = test_x;
14688       kill_y = test_y;
14689     }
14690     else if (test_element == EL_PENGUIN)
14691     {
14692       kill_x = test_x;
14693       kill_y = test_y;
14694     }
14695   }
14696
14697   if (kill_x != -1 || kill_y != -1)
14698   {
14699     if (IS_PLAYER(kill_x, kill_y))
14700     {
14701       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14702
14703       if (player->shield_deadly_time_left > 0 &&
14704           !IS_INDESTRUCTIBLE(bad_element))
14705         Bang(bad_x, bad_y);
14706       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14707         KillPlayer(player);
14708     }
14709     else
14710       Bang(kill_x, kill_y);
14711   }
14712 }
14713
14714 void TestIfPlayerTouchesBadThing(int x, int y)
14715 {
14716   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14717 }
14718
14719 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14720 {
14721   TestIfGoodThingHitsBadThing(x, y, move_dir);
14722 }
14723
14724 void TestIfBadThingTouchesPlayer(int x, int y)
14725 {
14726   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14727 }
14728
14729 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14730 {
14731   TestIfBadThingHitsGoodThing(x, y, move_dir);
14732 }
14733
14734 void TestIfFriendTouchesBadThing(int x, int y)
14735 {
14736   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14737 }
14738
14739 void TestIfBadThingTouchesFriend(int x, int y)
14740 {
14741   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14742 }
14743
14744 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14745 {
14746   int i, kill_x = bad_x, kill_y = bad_y;
14747   static int xy[4][2] =
14748   {
14749     { 0, -1 },
14750     { -1, 0 },
14751     { +1, 0 },
14752     { 0, +1 }
14753   };
14754
14755   for (i = 0; i < NUM_DIRECTIONS; i++)
14756   {
14757     int x, y, element;
14758
14759     x = bad_x + xy[i][0];
14760     y = bad_y + xy[i][1];
14761     if (!IN_LEV_FIELD(x, y))
14762       continue;
14763
14764     element = Feld[x][y];
14765     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14766         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14767     {
14768       kill_x = x;
14769       kill_y = y;
14770       break;
14771     }
14772   }
14773
14774   if (kill_x != bad_x || kill_y != bad_y)
14775     Bang(bad_x, bad_y);
14776 }
14777
14778 void KillPlayer(struct PlayerInfo *player)
14779 {
14780   int jx = player->jx, jy = player->jy;
14781
14782   if (!player->active)
14783     return;
14784
14785 #if 0
14786   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14787          player->killed, player->active, player->reanimated);
14788 #endif
14789
14790   /* the following code was introduced to prevent an infinite loop when calling
14791      -> Bang()
14792      -> CheckTriggeredElementChangeExt()
14793      -> ExecuteCustomElementAction()
14794      -> KillPlayer()
14795      -> (infinitely repeating the above sequence of function calls)
14796      which occurs when killing the player while having a CE with the setting
14797      "kill player X when explosion of <player X>"; the solution using a new
14798      field "player->killed" was chosen for backwards compatibility, although
14799      clever use of the fields "player->active" etc. would probably also work */
14800 #if 1
14801   if (player->killed)
14802     return;
14803 #endif
14804
14805   player->killed = TRUE;
14806
14807   /* remove accessible field at the player's position */
14808   Feld[jx][jy] = EL_EMPTY;
14809
14810   /* deactivate shield (else Bang()/Explode() would not work right) */
14811   player->shield_normal_time_left = 0;
14812   player->shield_deadly_time_left = 0;
14813
14814 #if 0
14815   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14816          player->killed, player->active, player->reanimated);
14817 #endif
14818
14819   Bang(jx, jy);
14820
14821 #if 0
14822   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14823          player->killed, player->active, player->reanimated);
14824 #endif
14825
14826 #if USE_PLAYER_REANIMATION
14827 #if 1
14828   if (player->reanimated)       /* killed player may have been reanimated */
14829     player->killed = player->reanimated = FALSE;
14830   else
14831     BuryPlayer(player);
14832 #else
14833   if (player->killed)           /* player may have been reanimated */
14834     BuryPlayer(player);
14835 #endif
14836 #else
14837   BuryPlayer(player);
14838 #endif
14839 }
14840
14841 static void KillPlayerUnlessEnemyProtected(int x, int y)
14842 {
14843   if (!PLAYER_ENEMY_PROTECTED(x, y))
14844     KillPlayer(PLAYERINFO(x, y));
14845 }
14846
14847 static void KillPlayerUnlessExplosionProtected(int x, int y)
14848 {
14849   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14850     KillPlayer(PLAYERINFO(x, y));
14851 }
14852
14853 void BuryPlayer(struct PlayerInfo *player)
14854 {
14855   int jx = player->jx, jy = player->jy;
14856
14857   if (!player->active)
14858     return;
14859
14860   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14861   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14862
14863   player->GameOver = TRUE;
14864   RemovePlayer(player);
14865 }
14866
14867 void RemovePlayer(struct PlayerInfo *player)
14868 {
14869   int jx = player->jx, jy = player->jy;
14870   int i, found = FALSE;
14871
14872   player->present = FALSE;
14873   player->active = FALSE;
14874
14875   if (!ExplodeField[jx][jy])
14876     StorePlayer[jx][jy] = 0;
14877
14878   if (player->is_moving)
14879     TEST_DrawLevelField(player->last_jx, player->last_jy);
14880
14881   for (i = 0; i < MAX_PLAYERS; i++)
14882     if (stored_player[i].active)
14883       found = TRUE;
14884
14885   if (!found)
14886     AllPlayersGone = TRUE;
14887
14888   ExitX = ZX = jx;
14889   ExitY = ZY = jy;
14890 }
14891
14892 #if USE_NEW_SNAP_DELAY
14893 static void setFieldForSnapping(int x, int y, int element, int direction)
14894 {
14895   struct ElementInfo *ei = &element_info[element];
14896   int direction_bit = MV_DIR_TO_BIT(direction);
14897   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14898   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14899                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14900
14901   Feld[x][y] = EL_ELEMENT_SNAPPING;
14902   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14903
14904   ResetGfxAnimation(x, y);
14905
14906   GfxElement[x][y] = element;
14907   GfxAction[x][y] = action;
14908   GfxDir[x][y] = direction;
14909   GfxFrame[x][y] = -1;
14910 }
14911 #endif
14912
14913 /*
14914   =============================================================================
14915   checkDiagonalPushing()
14916   -----------------------------------------------------------------------------
14917   check if diagonal input device direction results in pushing of object
14918   (by checking if the alternative direction is walkable, diggable, ...)
14919   =============================================================================
14920 */
14921
14922 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14923                                     int x, int y, int real_dx, int real_dy)
14924 {
14925   int jx, jy, dx, dy, xx, yy;
14926
14927   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14928     return TRUE;
14929
14930   /* diagonal direction: check alternative direction */
14931   jx = player->jx;
14932   jy = player->jy;
14933   dx = x - jx;
14934   dy = y - jy;
14935   xx = jx + (dx == 0 ? real_dx : 0);
14936   yy = jy + (dy == 0 ? real_dy : 0);
14937
14938   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14939 }
14940
14941 /*
14942   =============================================================================
14943   DigField()
14944   -----------------------------------------------------------------------------
14945   x, y:                 field next to player (non-diagonal) to try to dig to
14946   real_dx, real_dy:     direction as read from input device (can be diagonal)
14947   =============================================================================
14948 */
14949
14950 static int DigField(struct PlayerInfo *player,
14951                     int oldx, int oldy, int x, int y,
14952                     int real_dx, int real_dy, int mode)
14953 {
14954   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14955   boolean player_was_pushing = player->is_pushing;
14956   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14957   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14958   int jx = oldx, jy = oldy;
14959   int dx = x - jx, dy = y - jy;
14960   int nextx = x + dx, nexty = y + dy;
14961   int move_direction = (dx == -1 ? MV_LEFT  :
14962                         dx == +1 ? MV_RIGHT :
14963                         dy == -1 ? MV_UP    :
14964                         dy == +1 ? MV_DOWN  : MV_NONE);
14965   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14966   int dig_side = MV_DIR_OPPOSITE(move_direction);
14967   int old_element = Feld[jx][jy];
14968 #if USE_FIXED_DONT_RUN_INTO
14969   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14970 #else
14971   int element;
14972 #endif
14973   int collect_count;
14974
14975   if (is_player)                /* function can also be called by EL_PENGUIN */
14976   {
14977     if (player->MovPos == 0)
14978     {
14979       player->is_digging = FALSE;
14980       player->is_collecting = FALSE;
14981     }
14982
14983     if (player->MovPos == 0)    /* last pushing move finished */
14984       player->is_pushing = FALSE;
14985
14986     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14987     {
14988       player->is_switching = FALSE;
14989       player->push_delay = -1;
14990
14991       return MP_NO_ACTION;
14992     }
14993   }
14994
14995 #if !USE_FIXED_DONT_RUN_INTO
14996   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14997     return MP_NO_ACTION;
14998 #endif
14999
15000   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15001     old_element = Back[jx][jy];
15002
15003   /* in case of element dropped at player position, check background */
15004   else if (Back[jx][jy] != EL_EMPTY &&
15005            game.engine_version >= VERSION_IDENT(2,2,0,0))
15006     old_element = Back[jx][jy];
15007
15008   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15009     return MP_NO_ACTION;        /* field has no opening in this direction */
15010
15011   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15012     return MP_NO_ACTION;        /* field has no opening in this direction */
15013
15014 #if USE_FIXED_DONT_RUN_INTO
15015   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15016   {
15017     SplashAcid(x, y);
15018
15019     Feld[jx][jy] = player->artwork_element;
15020     InitMovingField(jx, jy, MV_DOWN);
15021     Store[jx][jy] = EL_ACID;
15022     ContinueMoving(jx, jy);
15023     BuryPlayer(player);
15024
15025     return MP_DONT_RUN_INTO;
15026   }
15027 #endif
15028
15029 #if USE_FIXED_DONT_RUN_INTO
15030   if (player_can_move && DONT_RUN_INTO(element))
15031   {
15032     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15033
15034     return MP_DONT_RUN_INTO;
15035   }
15036 #endif
15037
15038 #if USE_FIXED_DONT_RUN_INTO
15039   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15040     return MP_NO_ACTION;
15041 #endif
15042
15043 #if !USE_FIXED_DONT_RUN_INTO
15044   element = Feld[x][y];
15045 #endif
15046
15047   collect_count = element_info[element].collect_count_initial;
15048
15049   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15050     return MP_NO_ACTION;
15051
15052   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15053     player_can_move = player_can_move_or_snap;
15054
15055   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15056       game.engine_version >= VERSION_IDENT(2,2,0,0))
15057   {
15058     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15059                                player->index_bit, dig_side);
15060     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15061                                         player->index_bit, dig_side);
15062
15063     if (element == EL_DC_LANDMINE)
15064       Bang(x, y);
15065
15066     if (Feld[x][y] != element)          /* field changed by snapping */
15067       return MP_ACTION;
15068
15069     return MP_NO_ACTION;
15070   }
15071
15072 #if USE_PLAYER_GRAVITY
15073   if (player->gravity && is_player && !player->is_auto_moving &&
15074       canFallDown(player) && move_direction != MV_DOWN &&
15075       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15076     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15077 #else
15078   if (game.gravity && is_player && !player->is_auto_moving &&
15079       canFallDown(player) && move_direction != MV_DOWN &&
15080       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15081     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15082 #endif
15083
15084   if (player_can_move &&
15085       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15086   {
15087     int sound_element = SND_ELEMENT(element);
15088     int sound_action = ACTION_WALKING;
15089
15090     if (IS_RND_GATE(element))
15091     {
15092       if (!player->key[RND_GATE_NR(element)])
15093         return MP_NO_ACTION;
15094     }
15095     else if (IS_RND_GATE_GRAY(element))
15096     {
15097       if (!player->key[RND_GATE_GRAY_NR(element)])
15098         return MP_NO_ACTION;
15099     }
15100     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15101     {
15102       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15103         return MP_NO_ACTION;
15104     }
15105     else if (element == EL_EXIT_OPEN ||
15106              element == EL_EM_EXIT_OPEN ||
15107 #if 1
15108              element == EL_EM_EXIT_OPENING ||
15109 #endif
15110              element == EL_STEEL_EXIT_OPEN ||
15111              element == EL_EM_STEEL_EXIT_OPEN ||
15112 #if 1
15113              element == EL_EM_STEEL_EXIT_OPENING ||
15114 #endif
15115              element == EL_SP_EXIT_OPEN ||
15116              element == EL_SP_EXIT_OPENING)
15117     {
15118       sound_action = ACTION_PASSING;    /* player is passing exit */
15119     }
15120     else if (element == EL_EMPTY)
15121     {
15122       sound_action = ACTION_MOVING;             /* nothing to walk on */
15123     }
15124
15125     /* play sound from background or player, whatever is available */
15126     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15127       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15128     else
15129       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15130   }
15131   else if (player_can_move &&
15132            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15133   {
15134     if (!ACCESS_FROM(element, opposite_direction))
15135       return MP_NO_ACTION;      /* field not accessible from this direction */
15136
15137     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15138       return MP_NO_ACTION;
15139
15140     if (IS_EM_GATE(element))
15141     {
15142       if (!player->key[EM_GATE_NR(element)])
15143         return MP_NO_ACTION;
15144     }
15145     else if (IS_EM_GATE_GRAY(element))
15146     {
15147       if (!player->key[EM_GATE_GRAY_NR(element)])
15148         return MP_NO_ACTION;
15149     }
15150     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15151     {
15152       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15153         return MP_NO_ACTION;
15154     }
15155     else if (IS_EMC_GATE(element))
15156     {
15157       if (!player->key[EMC_GATE_NR(element)])
15158         return MP_NO_ACTION;
15159     }
15160     else if (IS_EMC_GATE_GRAY(element))
15161     {
15162       if (!player->key[EMC_GATE_GRAY_NR(element)])
15163         return MP_NO_ACTION;
15164     }
15165     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15166     {
15167       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15168         return MP_NO_ACTION;
15169     }
15170     else if (element == EL_DC_GATE_WHITE ||
15171              element == EL_DC_GATE_WHITE_GRAY ||
15172              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15173     {
15174       if (player->num_white_keys == 0)
15175         return MP_NO_ACTION;
15176
15177       player->num_white_keys--;
15178     }
15179     else if (IS_SP_PORT(element))
15180     {
15181       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15182           element == EL_SP_GRAVITY_PORT_RIGHT ||
15183           element == EL_SP_GRAVITY_PORT_UP ||
15184           element == EL_SP_GRAVITY_PORT_DOWN)
15185 #if USE_PLAYER_GRAVITY
15186         player->gravity = !player->gravity;
15187 #else
15188         game.gravity = !game.gravity;
15189 #endif
15190       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15191                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15192                element == EL_SP_GRAVITY_ON_PORT_UP ||
15193                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15194 #if USE_PLAYER_GRAVITY
15195         player->gravity = TRUE;
15196 #else
15197         game.gravity = TRUE;
15198 #endif
15199       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15200                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15201                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15202                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15203 #if USE_PLAYER_GRAVITY
15204         player->gravity = FALSE;
15205 #else
15206         game.gravity = FALSE;
15207 #endif
15208     }
15209
15210     /* automatically move to the next field with double speed */
15211     player->programmed_action = move_direction;
15212
15213     if (player->move_delay_reset_counter == 0)
15214     {
15215       player->move_delay_reset_counter = 2;     /* two double speed steps */
15216
15217       DOUBLE_PLAYER_SPEED(player);
15218     }
15219
15220     PlayLevelSoundAction(x, y, ACTION_PASSING);
15221   }
15222   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15223   {
15224     RemoveField(x, y);
15225
15226     if (mode != DF_SNAP)
15227     {
15228       GfxElement[x][y] = GFX_ELEMENT(element);
15229       player->is_digging = TRUE;
15230     }
15231
15232     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15233
15234     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15235                                         player->index_bit, dig_side);
15236
15237     if (mode == DF_SNAP)
15238     {
15239 #if USE_NEW_SNAP_DELAY
15240       if (level.block_snap_field)
15241         setFieldForSnapping(x, y, element, move_direction);
15242       else
15243         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15244 #else
15245       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15246 #endif
15247
15248       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15249                                           player->index_bit, dig_side);
15250     }
15251   }
15252   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15253   {
15254     RemoveField(x, y);
15255
15256     if (is_player && mode != DF_SNAP)
15257     {
15258       GfxElement[x][y] = element;
15259       player->is_collecting = TRUE;
15260     }
15261
15262     if (element == EL_SPEED_PILL)
15263     {
15264       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15265     }
15266     else if (element == EL_EXTRA_TIME && level.time > 0)
15267     {
15268       TimeLeft += level.extra_time;
15269
15270 #if 1
15271       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15272
15273       DisplayGameControlValues();
15274 #else
15275       DrawGameValue_Time(TimeLeft);
15276 #endif
15277     }
15278     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15279     {
15280       player->shield_normal_time_left += level.shield_normal_time;
15281       if (element == EL_SHIELD_DEADLY)
15282         player->shield_deadly_time_left += level.shield_deadly_time;
15283     }
15284     else if (element == EL_DYNAMITE ||
15285              element == EL_EM_DYNAMITE ||
15286              element == EL_SP_DISK_RED)
15287     {
15288       if (player->inventory_size < MAX_INVENTORY_SIZE)
15289         player->inventory_element[player->inventory_size++] = element;
15290
15291       DrawGameDoorValues();
15292     }
15293     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15294     {
15295       player->dynabomb_count++;
15296       player->dynabombs_left++;
15297     }
15298     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15299     {
15300       player->dynabomb_size++;
15301     }
15302     else if (element == EL_DYNABOMB_INCREASE_POWER)
15303     {
15304       player->dynabomb_xl = TRUE;
15305     }
15306     else if (IS_KEY(element))
15307     {
15308       player->key[KEY_NR(element)] = TRUE;
15309
15310       DrawGameDoorValues();
15311     }
15312     else if (element == EL_DC_KEY_WHITE)
15313     {
15314       player->num_white_keys++;
15315
15316       /* display white keys? */
15317       /* DrawGameDoorValues(); */
15318     }
15319     else if (IS_ENVELOPE(element))
15320     {
15321       player->show_envelope = element;
15322     }
15323     else if (element == EL_EMC_LENSES)
15324     {
15325       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15326
15327       RedrawAllInvisibleElementsForLenses();
15328     }
15329     else if (element == EL_EMC_MAGNIFIER)
15330     {
15331       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15332
15333       RedrawAllInvisibleElementsForMagnifier();
15334     }
15335     else if (IS_DROPPABLE(element) ||
15336              IS_THROWABLE(element))     /* can be collected and dropped */
15337     {
15338       int i;
15339
15340       if (collect_count == 0)
15341         player->inventory_infinite_element = element;
15342       else
15343         for (i = 0; i < collect_count; i++)
15344           if (player->inventory_size < MAX_INVENTORY_SIZE)
15345             player->inventory_element[player->inventory_size++] = element;
15346
15347       DrawGameDoorValues();
15348     }
15349     else if (collect_count > 0)
15350     {
15351       local_player->gems_still_needed -= collect_count;
15352       if (local_player->gems_still_needed < 0)
15353         local_player->gems_still_needed = 0;
15354
15355 #if 1
15356       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15357
15358       DisplayGameControlValues();
15359 #else
15360       DrawGameValue_Emeralds(local_player->gems_still_needed);
15361 #endif
15362     }
15363
15364     RaiseScoreElement(element);
15365     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15366
15367     if (is_player)
15368       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15369                                           player->index_bit, dig_side);
15370
15371     if (mode == DF_SNAP)
15372     {
15373 #if USE_NEW_SNAP_DELAY
15374       if (level.block_snap_field)
15375         setFieldForSnapping(x, y, element, move_direction);
15376       else
15377         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15378 #else
15379       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15380 #endif
15381
15382       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15383                                           player->index_bit, dig_side);
15384     }
15385   }
15386   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15387   {
15388     if (mode == DF_SNAP && element != EL_BD_ROCK)
15389       return MP_NO_ACTION;
15390
15391     if (CAN_FALL(element) && dy)
15392       return MP_NO_ACTION;
15393
15394     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15395         !(element == EL_SPRING && level.use_spring_bug))
15396       return MP_NO_ACTION;
15397
15398     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15399         ((move_direction & MV_VERTICAL &&
15400           ((element_info[element].move_pattern & MV_LEFT &&
15401             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15402            (element_info[element].move_pattern & MV_RIGHT &&
15403             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15404          (move_direction & MV_HORIZONTAL &&
15405           ((element_info[element].move_pattern & MV_UP &&
15406             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15407            (element_info[element].move_pattern & MV_DOWN &&
15408             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15409       return MP_NO_ACTION;
15410
15411     /* do not push elements already moving away faster than player */
15412     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15413         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15414       return MP_NO_ACTION;
15415
15416     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15417     {
15418       if (player->push_delay_value == -1 || !player_was_pushing)
15419         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15420     }
15421     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15422     {
15423       if (player->push_delay_value == -1)
15424         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15425     }
15426     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15427     {
15428       if (!player->is_pushing)
15429         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15430     }
15431
15432     player->is_pushing = TRUE;
15433     player->is_active = TRUE;
15434
15435     if (!(IN_LEV_FIELD(nextx, nexty) &&
15436           (IS_FREE(nextx, nexty) ||
15437            (IS_SB_ELEMENT(element) &&
15438             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15439            (IS_CUSTOM_ELEMENT(element) &&
15440             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15441       return MP_NO_ACTION;
15442
15443     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15444       return MP_NO_ACTION;
15445
15446     if (player->push_delay == -1)       /* new pushing; restart delay */
15447       player->push_delay = 0;
15448
15449     if (player->push_delay < player->push_delay_value &&
15450         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15451         element != EL_SPRING && element != EL_BALLOON)
15452     {
15453       /* make sure that there is no move delay before next try to push */
15454       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15455         player->move_delay = 0;
15456
15457       return MP_NO_ACTION;
15458     }
15459
15460     if (IS_CUSTOM_ELEMENT(element) &&
15461         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15462     {
15463       if (!DigFieldByCE(nextx, nexty, element))
15464         return MP_NO_ACTION;
15465     }
15466
15467     if (IS_SB_ELEMENT(element))
15468     {
15469       if (element == EL_SOKOBAN_FIELD_FULL)
15470       {
15471         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15472         local_player->sokobanfields_still_needed++;
15473       }
15474
15475       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15476       {
15477         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15478         local_player->sokobanfields_still_needed--;
15479       }
15480
15481       Feld[x][y] = EL_SOKOBAN_OBJECT;
15482
15483       if (Back[x][y] == Back[nextx][nexty])
15484         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15485       else if (Back[x][y] != 0)
15486         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15487                                     ACTION_EMPTYING);
15488       else
15489         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15490                                     ACTION_FILLING);
15491
15492 #if 1
15493       if (local_player->sokobanfields_still_needed == 0 &&
15494           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15495 #else
15496       if (local_player->sokobanfields_still_needed == 0 &&
15497           game.emulation == EMU_SOKOBAN)
15498 #endif
15499       {
15500         PlayerWins(player);
15501
15502         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15503       }
15504     }
15505     else
15506       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15507
15508     InitMovingField(x, y, move_direction);
15509     GfxAction[x][y] = ACTION_PUSHING;
15510
15511     if (mode == DF_SNAP)
15512       ContinueMoving(x, y);
15513     else
15514       MovPos[x][y] = (dx != 0 ? dx : dy);
15515
15516     Pushed[x][y] = TRUE;
15517     Pushed[nextx][nexty] = TRUE;
15518
15519     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15520       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15521     else
15522       player->push_delay_value = -1;    /* get new value later */
15523
15524     /* check for element change _after_ element has been pushed */
15525     if (game.use_change_when_pushing_bug)
15526     {
15527       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15528                                  player->index_bit, dig_side);
15529       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15530                                           player->index_bit, dig_side);
15531     }
15532   }
15533   else if (IS_SWITCHABLE(element))
15534   {
15535     if (PLAYER_SWITCHING(player, x, y))
15536     {
15537       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15538                                           player->index_bit, dig_side);
15539
15540       return MP_ACTION;
15541     }
15542
15543     player->is_switching = TRUE;
15544     player->switch_x = x;
15545     player->switch_y = y;
15546
15547     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15548
15549     if (element == EL_ROBOT_WHEEL)
15550     {
15551       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15552       ZX = x;
15553       ZY = y;
15554
15555       game.robot_wheel_active = TRUE;
15556
15557       TEST_DrawLevelField(x, y);
15558     }
15559     else if (element == EL_SP_TERMINAL)
15560     {
15561       int xx, yy;
15562
15563       SCAN_PLAYFIELD(xx, yy)
15564       {
15565         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15566           Bang(xx, yy);
15567         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15568           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15569       }
15570     }
15571     else if (IS_BELT_SWITCH(element))
15572     {
15573       ToggleBeltSwitch(x, y);
15574     }
15575     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15576              element == EL_SWITCHGATE_SWITCH_DOWN ||
15577              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15578              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15579     {
15580       ToggleSwitchgateSwitch(x, y);
15581     }
15582     else if (element == EL_LIGHT_SWITCH ||
15583              element == EL_LIGHT_SWITCH_ACTIVE)
15584     {
15585       ToggleLightSwitch(x, y);
15586     }
15587     else if (element == EL_TIMEGATE_SWITCH ||
15588              element == EL_DC_TIMEGATE_SWITCH)
15589     {
15590       ActivateTimegateSwitch(x, y);
15591     }
15592     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15593              element == EL_BALLOON_SWITCH_RIGHT ||
15594              element == EL_BALLOON_SWITCH_UP    ||
15595              element == EL_BALLOON_SWITCH_DOWN  ||
15596              element == EL_BALLOON_SWITCH_NONE  ||
15597              element == EL_BALLOON_SWITCH_ANY)
15598     {
15599       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15600                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15601                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15602                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15603                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15604                              move_direction);
15605     }
15606     else if (element == EL_LAMP)
15607     {
15608       Feld[x][y] = EL_LAMP_ACTIVE;
15609       local_player->lights_still_needed--;
15610
15611       ResetGfxAnimation(x, y);
15612       TEST_DrawLevelField(x, y);
15613     }
15614     else if (element == EL_TIME_ORB_FULL)
15615     {
15616       Feld[x][y] = EL_TIME_ORB_EMPTY;
15617
15618       if (level.time > 0 || level.use_time_orb_bug)
15619       {
15620         TimeLeft += level.time_orb_time;
15621
15622 #if 1
15623         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15624
15625         DisplayGameControlValues();
15626 #else
15627         DrawGameValue_Time(TimeLeft);
15628 #endif
15629       }
15630
15631       ResetGfxAnimation(x, y);
15632       TEST_DrawLevelField(x, y);
15633     }
15634     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15635              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15636     {
15637       int xx, yy;
15638
15639       game.ball_state = !game.ball_state;
15640
15641       SCAN_PLAYFIELD(xx, yy)
15642       {
15643         int e = Feld[xx][yy];
15644
15645         if (game.ball_state)
15646         {
15647           if (e == EL_EMC_MAGIC_BALL)
15648             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15649           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15650             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15651         }
15652         else
15653         {
15654           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15655             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15656           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15657             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15658         }
15659       }
15660     }
15661
15662     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15663                                         player->index_bit, dig_side);
15664
15665     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15666                                         player->index_bit, dig_side);
15667
15668     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15669                                         player->index_bit, dig_side);
15670
15671     return MP_ACTION;
15672   }
15673   else
15674   {
15675     if (!PLAYER_SWITCHING(player, x, y))
15676     {
15677       player->is_switching = TRUE;
15678       player->switch_x = x;
15679       player->switch_y = y;
15680
15681       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15682                                  player->index_bit, dig_side);
15683       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15684                                           player->index_bit, dig_side);
15685
15686       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15687                                  player->index_bit, dig_side);
15688       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15689                                           player->index_bit, dig_side);
15690     }
15691
15692     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15693                                player->index_bit, dig_side);
15694     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15695                                         player->index_bit, dig_side);
15696
15697     return MP_NO_ACTION;
15698   }
15699
15700   player->push_delay = -1;
15701
15702   if (is_player)                /* function can also be called by EL_PENGUIN */
15703   {
15704     if (Feld[x][y] != element)          /* really digged/collected something */
15705     {
15706       player->is_collecting = !player->is_digging;
15707       player->is_active = TRUE;
15708     }
15709   }
15710
15711   return MP_MOVING;
15712 }
15713
15714 static boolean DigFieldByCE(int x, int y, int digging_element)
15715 {
15716   int element = Feld[x][y];
15717
15718   if (!IS_FREE(x, y))
15719   {
15720     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15721                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15722                   ACTION_BREAKING);
15723
15724     /* no element can dig solid indestructible elements */
15725     if (IS_INDESTRUCTIBLE(element) &&
15726         !IS_DIGGABLE(element) &&
15727         !IS_COLLECTIBLE(element))
15728       return FALSE;
15729
15730     if (AmoebaNr[x][y] &&
15731         (element == EL_AMOEBA_FULL ||
15732          element == EL_BD_AMOEBA ||
15733          element == EL_AMOEBA_GROWING))
15734     {
15735       AmoebaCnt[AmoebaNr[x][y]]--;
15736       AmoebaCnt2[AmoebaNr[x][y]]--;
15737     }
15738
15739     if (IS_MOVING(x, y))
15740       RemoveMovingField(x, y);
15741     else
15742     {
15743       RemoveField(x, y);
15744       TEST_DrawLevelField(x, y);
15745     }
15746
15747     /* if digged element was about to explode, prevent the explosion */
15748     ExplodeField[x][y] = EX_TYPE_NONE;
15749
15750     PlayLevelSoundAction(x, y, action);
15751   }
15752
15753   Store[x][y] = EL_EMPTY;
15754
15755 #if 1
15756   /* this makes it possible to leave the removed element again */
15757   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15758     Store[x][y] = element;
15759 #else
15760   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15761   {
15762     int move_leave_element = element_info[digging_element].move_leave_element;
15763
15764     /* this makes it possible to leave the removed element again */
15765     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15766                    element : move_leave_element);
15767   }
15768 #endif
15769
15770   return TRUE;
15771 }
15772
15773 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15774 {
15775   int jx = player->jx, jy = player->jy;
15776   int x = jx + dx, y = jy + dy;
15777   int snap_direction = (dx == -1 ? MV_LEFT  :
15778                         dx == +1 ? MV_RIGHT :
15779                         dy == -1 ? MV_UP    :
15780                         dy == +1 ? MV_DOWN  : MV_NONE);
15781   boolean can_continue_snapping = (level.continuous_snapping &&
15782                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15783
15784   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15785     return FALSE;
15786
15787   if (!player->active || !IN_LEV_FIELD(x, y))
15788     return FALSE;
15789
15790   if (dx && dy)
15791     return FALSE;
15792
15793   if (!dx && !dy)
15794   {
15795     if (player->MovPos == 0)
15796       player->is_pushing = FALSE;
15797
15798     player->is_snapping = FALSE;
15799
15800     if (player->MovPos == 0)
15801     {
15802       player->is_moving = FALSE;
15803       player->is_digging = FALSE;
15804       player->is_collecting = FALSE;
15805     }
15806
15807     return FALSE;
15808   }
15809
15810 #if USE_NEW_CONTINUOUS_SNAPPING
15811   /* prevent snapping with already pressed snap key when not allowed */
15812   if (player->is_snapping && !can_continue_snapping)
15813     return FALSE;
15814 #else
15815   if (player->is_snapping)
15816     return FALSE;
15817 #endif
15818
15819   player->MovDir = snap_direction;
15820
15821   if (player->MovPos == 0)
15822   {
15823     player->is_moving = FALSE;
15824     player->is_digging = FALSE;
15825     player->is_collecting = FALSE;
15826   }
15827
15828   player->is_dropping = FALSE;
15829   player->is_dropping_pressed = FALSE;
15830   player->drop_pressed_delay = 0;
15831
15832   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15833     return FALSE;
15834
15835   player->is_snapping = TRUE;
15836   player->is_active = TRUE;
15837
15838   if (player->MovPos == 0)
15839   {
15840     player->is_moving = FALSE;
15841     player->is_digging = FALSE;
15842     player->is_collecting = FALSE;
15843   }
15844
15845   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15846     TEST_DrawLevelField(player->last_jx, player->last_jy);
15847
15848   TEST_DrawLevelField(x, y);
15849
15850   return TRUE;
15851 }
15852
15853 static boolean DropElement(struct PlayerInfo *player)
15854 {
15855   int old_element, new_element;
15856   int dropx = player->jx, dropy = player->jy;
15857   int drop_direction = player->MovDir;
15858   int drop_side = drop_direction;
15859 #if 1
15860   int drop_element = get_next_dropped_element(player);
15861 #else
15862   int drop_element = (player->inventory_size > 0 ?
15863                       player->inventory_element[player->inventory_size - 1] :
15864                       player->inventory_infinite_element != EL_UNDEFINED ?
15865                       player->inventory_infinite_element :
15866                       player->dynabombs_left > 0 ?
15867                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15868                       EL_UNDEFINED);
15869 #endif
15870
15871   player->is_dropping_pressed = TRUE;
15872
15873   /* do not drop an element on top of another element; when holding drop key
15874      pressed without moving, dropped element must move away before the next
15875      element can be dropped (this is especially important if the next element
15876      is dynamite, which can be placed on background for historical reasons) */
15877   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15878     return MP_ACTION;
15879
15880   if (IS_THROWABLE(drop_element))
15881   {
15882     dropx += GET_DX_FROM_DIR(drop_direction);
15883     dropy += GET_DY_FROM_DIR(drop_direction);
15884
15885     if (!IN_LEV_FIELD(dropx, dropy))
15886       return FALSE;
15887   }
15888
15889   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15890   new_element = drop_element;           /* default: no change when dropping */
15891
15892   /* check if player is active, not moving and ready to drop */
15893   if (!player->active || player->MovPos || player->drop_delay > 0)
15894     return FALSE;
15895
15896   /* check if player has anything that can be dropped */
15897   if (new_element == EL_UNDEFINED)
15898     return FALSE;
15899
15900   /* check if drop key was pressed long enough for EM style dynamite */
15901   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15902     return FALSE;
15903
15904   /* check if anything can be dropped at the current position */
15905   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15906     return FALSE;
15907
15908   /* collected custom elements can only be dropped on empty fields */
15909   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15910     return FALSE;
15911
15912   if (old_element != EL_EMPTY)
15913     Back[dropx][dropy] = old_element;   /* store old element on this field */
15914
15915   ResetGfxAnimation(dropx, dropy);
15916   ResetRandomAnimationValue(dropx, dropy);
15917
15918   if (player->inventory_size > 0 ||
15919       player->inventory_infinite_element != EL_UNDEFINED)
15920   {
15921     if (player->inventory_size > 0)
15922     {
15923       player->inventory_size--;
15924
15925       DrawGameDoorValues();
15926
15927       if (new_element == EL_DYNAMITE)
15928         new_element = EL_DYNAMITE_ACTIVE;
15929       else if (new_element == EL_EM_DYNAMITE)
15930         new_element = EL_EM_DYNAMITE_ACTIVE;
15931       else if (new_element == EL_SP_DISK_RED)
15932         new_element = EL_SP_DISK_RED_ACTIVE;
15933     }
15934
15935     Feld[dropx][dropy] = new_element;
15936
15937     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15938       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15939                           el2img(Feld[dropx][dropy]), 0);
15940
15941     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15942
15943     /* needed if previous element just changed to "empty" in the last frame */
15944     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15945
15946     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15947                                player->index_bit, drop_side);
15948     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15949                                         CE_PLAYER_DROPS_X,
15950                                         player->index_bit, drop_side);
15951
15952     TestIfElementTouchesCustomElement(dropx, dropy);
15953   }
15954   else          /* player is dropping a dyna bomb */
15955   {
15956     player->dynabombs_left--;
15957
15958     Feld[dropx][dropy] = new_element;
15959
15960     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15961       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15962                           el2img(Feld[dropx][dropy]), 0);
15963
15964     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15965   }
15966
15967   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15968     InitField_WithBug1(dropx, dropy, FALSE);
15969
15970   new_element = Feld[dropx][dropy];     /* element might have changed */
15971
15972   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15973       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15974   {
15975     int move_direction, nextx, nexty;
15976
15977     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15978       MovDir[dropx][dropy] = drop_direction;
15979
15980     move_direction = MovDir[dropx][dropy];
15981     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15982     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15983
15984     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15985
15986 #if USE_FIX_IMPACT_COLLISION
15987     /* do not cause impact style collision by dropping elements that can fall */
15988     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15989 #else
15990     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15991 #endif
15992   }
15993
15994   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15995   player->is_dropping = TRUE;
15996
15997   player->drop_pressed_delay = 0;
15998   player->is_dropping_pressed = FALSE;
15999
16000   player->drop_x = dropx;
16001   player->drop_y = dropy;
16002
16003   return TRUE;
16004 }
16005
16006 /* ------------------------------------------------------------------------- */
16007 /* game sound playing functions                                              */
16008 /* ------------------------------------------------------------------------- */
16009
16010 static int *loop_sound_frame = NULL;
16011 static int *loop_sound_volume = NULL;
16012
16013 void InitPlayLevelSound()
16014 {
16015   int num_sounds = getSoundListSize();
16016
16017   checked_free(loop_sound_frame);
16018   checked_free(loop_sound_volume);
16019
16020   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16021   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16022 }
16023
16024 static void PlayLevelSound(int x, int y, int nr)
16025 {
16026   int sx = SCREENX(x), sy = SCREENY(y);
16027   int volume, stereo_position;
16028   int max_distance = 8;
16029   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16030
16031   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16032       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16033     return;
16034
16035   if (!IN_LEV_FIELD(x, y) ||
16036       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16037       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16038     return;
16039
16040   volume = SOUND_MAX_VOLUME;
16041
16042   if (!IN_SCR_FIELD(sx, sy))
16043   {
16044     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16045     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16046
16047     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16048   }
16049
16050   stereo_position = (SOUND_MAX_LEFT +
16051                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16052                      (SCR_FIELDX + 2 * max_distance));
16053
16054   if (IS_LOOP_SOUND(nr))
16055   {
16056     /* This assures that quieter loop sounds do not overwrite louder ones,
16057        while restarting sound volume comparison with each new game frame. */
16058
16059     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16060       return;
16061
16062     loop_sound_volume[nr] = volume;
16063     loop_sound_frame[nr] = FrameCounter;
16064   }
16065
16066   PlaySoundExt(nr, volume, stereo_position, type);
16067 }
16068
16069 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16070 {
16071   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16072                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16073                  y < LEVELY(BY1) ? LEVELY(BY1) :
16074                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16075                  sound_action);
16076 }
16077
16078 static void PlayLevelSoundAction(int x, int y, int action)
16079 {
16080   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16081 }
16082
16083 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16084 {
16085   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16086
16087   if (sound_effect != SND_UNDEFINED)
16088     PlayLevelSound(x, y, sound_effect);
16089 }
16090
16091 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16092                                               int action)
16093 {
16094   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16095
16096   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16097     PlayLevelSound(x, y, sound_effect);
16098 }
16099
16100 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16101 {
16102   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16103
16104   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16105     PlayLevelSound(x, y, sound_effect);
16106 }
16107
16108 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16109 {
16110   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16111
16112   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16113     StopSound(sound_effect);
16114 }
16115
16116 static void PlayLevelMusic()
16117 {
16118   if (levelset.music[level_nr] != MUS_UNDEFINED)
16119     PlayMusic(levelset.music[level_nr]);        /* from config file */
16120   else
16121     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16122 }
16123
16124 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16125 {
16126   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16127   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16128   int x = xx - 1 - offset;
16129   int y = yy - 1 - offset;
16130
16131   switch (sample)
16132   {
16133     case SAMPLE_blank:
16134       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16135       break;
16136
16137     case SAMPLE_roll:
16138       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16139       break;
16140
16141     case SAMPLE_stone:
16142       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16143       break;
16144
16145     case SAMPLE_nut:
16146       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16147       break;
16148
16149     case SAMPLE_crack:
16150       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16151       break;
16152
16153     case SAMPLE_bug:
16154       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16155       break;
16156
16157     case SAMPLE_tank:
16158       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16159       break;
16160
16161     case SAMPLE_android_clone:
16162       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16163       break;
16164
16165     case SAMPLE_android_move:
16166       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16167       break;
16168
16169     case SAMPLE_spring:
16170       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16171       break;
16172
16173     case SAMPLE_slurp:
16174       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16175       break;
16176
16177     case SAMPLE_eater:
16178       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16179       break;
16180
16181     case SAMPLE_eater_eat:
16182       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16183       break;
16184
16185     case SAMPLE_alien:
16186       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16187       break;
16188
16189     case SAMPLE_collect:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16191       break;
16192
16193     case SAMPLE_diamond:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16195       break;
16196
16197     case SAMPLE_squash:
16198       /* !!! CHECK THIS !!! */
16199 #if 1
16200       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16201 #else
16202       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16203 #endif
16204       break;
16205
16206     case SAMPLE_wonderfall:
16207       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16208       break;
16209
16210     case SAMPLE_drip:
16211       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16212       break;
16213
16214     case SAMPLE_push:
16215       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16216       break;
16217
16218     case SAMPLE_dirt:
16219       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16220       break;
16221
16222     case SAMPLE_acid:
16223       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16224       break;
16225
16226     case SAMPLE_ball:
16227       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16228       break;
16229
16230     case SAMPLE_grow:
16231       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16232       break;
16233
16234     case SAMPLE_wonder:
16235       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16236       break;
16237
16238     case SAMPLE_door:
16239       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16240       break;
16241
16242     case SAMPLE_exit_open:
16243       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16244       break;
16245
16246     case SAMPLE_exit_leave:
16247       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16248       break;
16249
16250     case SAMPLE_dynamite:
16251       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16252       break;
16253
16254     case SAMPLE_tick:
16255       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16256       break;
16257
16258     case SAMPLE_press:
16259       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16260       break;
16261
16262     case SAMPLE_wheel:
16263       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16264       break;
16265
16266     case SAMPLE_boom:
16267       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16268       break;
16269
16270     case SAMPLE_die:
16271       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16272       break;
16273
16274     case SAMPLE_time:
16275       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16276       break;
16277
16278     default:
16279       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16280       break;
16281   }
16282 }
16283
16284 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16285 {
16286   int element = map_element_SP_to_RND(element_sp);
16287   int action = map_action_SP_to_RND(action_sp);
16288   int offset = (setup.sp_show_border_elements ? 0 : 1);
16289   int x = xx - offset;
16290   int y = yy - offset;
16291
16292 #if 0
16293   printf("::: %d -> %d\n", element_sp, action_sp);
16294 #endif
16295
16296   PlayLevelSoundElementAction(x, y, element, action);
16297 }
16298
16299 #if 0
16300 void ChangeTime(int value)
16301 {
16302   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16303
16304   *time += value;
16305
16306   /* EMC game engine uses value from time counter of RND game engine */
16307   level.native_em_level->lev->time = *time;
16308
16309   DrawGameValue_Time(*time);
16310 }
16311
16312 void RaiseScore(int value)
16313 {
16314   /* EMC game engine and RND game engine have separate score counters */
16315   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16316                 &level.native_em_level->lev->score : &local_player->score);
16317
16318   *score += value;
16319
16320   DrawGameValue_Score(*score);
16321 }
16322 #endif
16323
16324 void RaiseScore(int value)
16325 {
16326   local_player->score += value;
16327
16328 #if 1
16329   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16330
16331   DisplayGameControlValues();
16332 #else
16333   DrawGameValue_Score(local_player->score);
16334 #endif
16335 }
16336
16337 void RaiseScoreElement(int element)
16338 {
16339   switch (element)
16340   {
16341     case EL_EMERALD:
16342     case EL_BD_DIAMOND:
16343     case EL_EMERALD_YELLOW:
16344     case EL_EMERALD_RED:
16345     case EL_EMERALD_PURPLE:
16346     case EL_SP_INFOTRON:
16347       RaiseScore(level.score[SC_EMERALD]);
16348       break;
16349     case EL_DIAMOND:
16350       RaiseScore(level.score[SC_DIAMOND]);
16351       break;
16352     case EL_CRYSTAL:
16353       RaiseScore(level.score[SC_CRYSTAL]);
16354       break;
16355     case EL_PEARL:
16356       RaiseScore(level.score[SC_PEARL]);
16357       break;
16358     case EL_BUG:
16359     case EL_BD_BUTTERFLY:
16360     case EL_SP_ELECTRON:
16361       RaiseScore(level.score[SC_BUG]);
16362       break;
16363     case EL_SPACESHIP:
16364     case EL_BD_FIREFLY:
16365     case EL_SP_SNIKSNAK:
16366       RaiseScore(level.score[SC_SPACESHIP]);
16367       break;
16368     case EL_YAMYAM:
16369     case EL_DARK_YAMYAM:
16370       RaiseScore(level.score[SC_YAMYAM]);
16371       break;
16372     case EL_ROBOT:
16373       RaiseScore(level.score[SC_ROBOT]);
16374       break;
16375     case EL_PACMAN:
16376       RaiseScore(level.score[SC_PACMAN]);
16377       break;
16378     case EL_NUT:
16379       RaiseScore(level.score[SC_NUT]);
16380       break;
16381     case EL_DYNAMITE:
16382     case EL_EM_DYNAMITE:
16383     case EL_SP_DISK_RED:
16384     case EL_DYNABOMB_INCREASE_NUMBER:
16385     case EL_DYNABOMB_INCREASE_SIZE:
16386     case EL_DYNABOMB_INCREASE_POWER:
16387       RaiseScore(level.score[SC_DYNAMITE]);
16388       break;
16389     case EL_SHIELD_NORMAL:
16390     case EL_SHIELD_DEADLY:
16391       RaiseScore(level.score[SC_SHIELD]);
16392       break;
16393     case EL_EXTRA_TIME:
16394       RaiseScore(level.extra_time_score);
16395       break;
16396     case EL_KEY_1:
16397     case EL_KEY_2:
16398     case EL_KEY_3:
16399     case EL_KEY_4:
16400     case EL_EM_KEY_1:
16401     case EL_EM_KEY_2:
16402     case EL_EM_KEY_3:
16403     case EL_EM_KEY_4:
16404     case EL_EMC_KEY_5:
16405     case EL_EMC_KEY_6:
16406     case EL_EMC_KEY_7:
16407     case EL_EMC_KEY_8:
16408     case EL_DC_KEY_WHITE:
16409       RaiseScore(level.score[SC_KEY]);
16410       break;
16411     default:
16412       RaiseScore(element_info[element].collect_score);
16413       break;
16414   }
16415 }
16416
16417 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16418 {
16419   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16420   {
16421 #if defined(NETWORK_AVALIABLE)
16422     if (options.network)
16423       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16424     else
16425 #endif
16426     {
16427       if (quick_quit)
16428       {
16429 #if 1
16430
16431 #if 1
16432         FadeSkipNextFadeIn();
16433 #else
16434         fading = fading_none;
16435 #endif
16436
16437 #else
16438         OpenDoor(DOOR_CLOSE_1);
16439 #endif
16440
16441         game_status = GAME_MODE_MAIN;
16442
16443 #if 1
16444         DrawAndFadeInMainMenu(REDRAW_FIELD);
16445 #else
16446         DrawMainMenu();
16447 #endif
16448       }
16449       else
16450       {
16451 #if 0
16452         FadeOut(REDRAW_FIELD);
16453 #endif
16454
16455         game_status = GAME_MODE_MAIN;
16456
16457         DrawAndFadeInMainMenu(REDRAW_FIELD);
16458       }
16459     }
16460   }
16461   else          /* continue playing the game */
16462   {
16463     if (tape.playing && tape.deactivate_display)
16464       TapeDeactivateDisplayOff(TRUE);
16465
16466     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16467
16468     if (tape.playing && tape.deactivate_display)
16469       TapeDeactivateDisplayOn();
16470   }
16471 }
16472
16473 void RequestQuitGame(boolean ask_if_really_quit)
16474 {
16475   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16476   boolean skip_request = AllPlayersGone || quick_quit;
16477
16478   RequestQuitGameExt(skip_request, quick_quit,
16479                      "Do you really want to quit the game ?");
16480 }
16481
16482
16483 /* ------------------------------------------------------------------------- */
16484 /* random generator functions                                                */
16485 /* ------------------------------------------------------------------------- */
16486
16487 unsigned int InitEngineRandom_RND(long seed)
16488 {
16489   game.num_random_calls = 0;
16490
16491 #if 0
16492   unsigned int rnd_seed = InitEngineRandom(seed);
16493
16494   printf("::: START RND: %d\n", rnd_seed);
16495
16496   return rnd_seed;
16497 #else
16498
16499   return InitEngineRandom(seed);
16500
16501 #endif
16502
16503 }
16504
16505 unsigned int RND(int max)
16506 {
16507   if (max > 0)
16508   {
16509     game.num_random_calls++;
16510
16511     return GetEngineRandom(max);
16512   }
16513
16514   return 0;
16515 }
16516
16517
16518 /* ------------------------------------------------------------------------- */
16519 /* game engine snapshot handling functions                                   */
16520 /* ------------------------------------------------------------------------- */
16521
16522 struct EngineSnapshotInfo
16523 {
16524   /* runtime values for custom element collect score */
16525   int collect_score[NUM_CUSTOM_ELEMENTS];
16526
16527   /* runtime values for group element choice position */
16528   int choice_pos[NUM_GROUP_ELEMENTS];
16529
16530   /* runtime values for belt position animations */
16531   int belt_graphic[4][NUM_BELT_PARTS];
16532   int belt_anim_mode[4][NUM_BELT_PARTS];
16533 };
16534
16535 static struct EngineSnapshotInfo engine_snapshot_rnd;
16536 static char *snapshot_level_identifier = NULL;
16537 static int snapshot_level_nr = -1;
16538
16539 static void SaveEngineSnapshotValues_RND()
16540 {
16541   static int belt_base_active_element[4] =
16542   {
16543     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16544     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16545     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16546     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16547   };
16548   int i, j;
16549
16550   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16551   {
16552     int element = EL_CUSTOM_START + i;
16553
16554     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16555   }
16556
16557   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16558   {
16559     int element = EL_GROUP_START + i;
16560
16561     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16562   }
16563
16564   for (i = 0; i < 4; i++)
16565   {
16566     for (j = 0; j < NUM_BELT_PARTS; j++)
16567     {
16568       int element = belt_base_active_element[i] + j;
16569       int graphic = el2img(element);
16570       int anim_mode = graphic_info[graphic].anim_mode;
16571
16572       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16573       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16574     }
16575   }
16576 }
16577
16578 static void LoadEngineSnapshotValues_RND()
16579 {
16580   unsigned long num_random_calls = game.num_random_calls;
16581   int i, j;
16582
16583   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16584   {
16585     int element = EL_CUSTOM_START + i;
16586
16587     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16588   }
16589
16590   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16591   {
16592     int element = EL_GROUP_START + i;
16593
16594     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16595   }
16596
16597   for (i = 0; i < 4; i++)
16598   {
16599     for (j = 0; j < NUM_BELT_PARTS; j++)
16600     {
16601       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16602       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16603
16604       graphic_info[graphic].anim_mode = anim_mode;
16605     }
16606   }
16607
16608   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16609   {
16610     InitRND(tape.random_seed);
16611     for (i = 0; i < num_random_calls; i++)
16612       RND(1);
16613   }
16614
16615   if (game.num_random_calls != num_random_calls)
16616   {
16617     Error(ERR_INFO, "number of random calls out of sync");
16618     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16619     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16620     Error(ERR_EXIT, "this should not happen -- please debug");
16621   }
16622 }
16623
16624 void SaveEngineSnapshot()
16625 {
16626   /* do not save snapshots from editor */
16627   if (level_editor_test_game)
16628     return;
16629
16630   /* free previous snapshot buffers, if needed */
16631   FreeEngineSnapshotBuffers();
16632
16633   /* copy some special values to a structure better suited for the snapshot */
16634
16635   SaveEngineSnapshotValues_RND();
16636   SaveEngineSnapshotValues_EM();
16637   SaveEngineSnapshotValues_SP();
16638
16639   /* save values stored in special snapshot structure */
16640
16641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16644
16645   /* save further RND engine values */
16646
16647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16650
16651   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16655
16656   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16657   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16658   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16659   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16660   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16661
16662   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16665
16666   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16667
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16669
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16672
16673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16691
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16694
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16698
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16701
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16707
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16710
16711   /* save level identification information */
16712
16713   setString(&snapshot_level_identifier, leveldir_current->identifier);
16714   snapshot_level_nr = level_nr;
16715
16716 #if 0
16717   ListNode *node = engine_snapshot_list_rnd;
16718   int num_bytes = 0;
16719
16720   while (node != NULL)
16721   {
16722     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16723
16724     node = node->next;
16725   }
16726
16727   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16728 #endif
16729 }
16730
16731 void LoadEngineSnapshot()
16732 {
16733   /* restore generically stored snapshot buffers */
16734
16735   LoadEngineSnapshotBuffers();
16736
16737   /* restore special values from snapshot structure */
16738
16739   LoadEngineSnapshotValues_RND();
16740   LoadEngineSnapshotValues_EM();
16741   LoadEngineSnapshotValues_SP();
16742 }
16743
16744 boolean CheckEngineSnapshot()
16745 {
16746   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16747           snapshot_level_nr == level_nr);
16748 }
16749
16750
16751 /* ---------- new game button stuff ---------------------------------------- */
16752
16753 static struct
16754 {
16755   int graphic;
16756   struct Rect *pos;
16757   int gadget_id;
16758   char *infotext;
16759 } gamebutton_info[NUM_GAME_BUTTONS] =
16760 {
16761   {
16762     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16763     GAME_CTRL_ID_STOP,                  "stop game"
16764   },
16765   {
16766     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16767     GAME_CTRL_ID_PAUSE,                 "pause game"
16768   },
16769   {
16770     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16771     GAME_CTRL_ID_PLAY,                  "play game"
16772   },
16773   {
16774     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16775     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16776   },
16777   {
16778     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16779     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16780   },
16781   {
16782     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16783     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16784   }
16785 };
16786
16787 void CreateGameButtons()
16788 {
16789   int i;
16790
16791   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16792   {
16793     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16794     struct Rect *pos = gamebutton_info[i].pos;
16795     struct GadgetInfo *gi;
16796     int button_type;
16797     boolean checked;
16798     unsigned long event_mask;
16799     int gd_x   = gfx->src_x;
16800     int gd_y   = gfx->src_y;
16801     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16802     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16803     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16804     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16805     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16806     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16807     int id = i;
16808
16809     if (id == GAME_CTRL_ID_STOP ||
16810         id == GAME_CTRL_ID_PAUSE ||
16811         id == GAME_CTRL_ID_PLAY)
16812     {
16813       button_type = GD_TYPE_NORMAL_BUTTON;
16814       checked = FALSE;
16815       event_mask = GD_EVENT_RELEASED;
16816     }
16817     else
16818     {
16819       button_type = GD_TYPE_CHECK_BUTTON;
16820       checked =
16821         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16822          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16823          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16824       event_mask = GD_EVENT_PRESSED;
16825     }
16826
16827     gi = CreateGadget(GDI_CUSTOM_ID, id,
16828                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16829                       GDI_X, DX + pos->x,
16830                       GDI_Y, DY + pos->y,
16831                       GDI_WIDTH, gfx->width,
16832                       GDI_HEIGHT, gfx->height,
16833                       GDI_TYPE, button_type,
16834                       GDI_STATE, GD_BUTTON_UNPRESSED,
16835                       GDI_CHECKED, checked,
16836                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16837                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16838                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16839                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16840                       GDI_DIRECT_DRAW, FALSE,
16841                       GDI_EVENT_MASK, event_mask,
16842                       GDI_CALLBACK_ACTION, HandleGameButtons,
16843                       GDI_END);
16844
16845     if (gi == NULL)
16846       Error(ERR_EXIT, "cannot create gadget");
16847
16848     game_gadget[id] = gi;
16849   }
16850 }
16851
16852 void FreeGameButtons()
16853 {
16854   int i;
16855
16856   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16857     FreeGadget(game_gadget[i]);
16858 }
16859
16860 static void MapGameButtons()
16861 {
16862   int i;
16863
16864   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16865     MapGadget(game_gadget[i]);
16866 }
16867
16868 void UnmapGameButtons()
16869 {
16870   int i;
16871
16872   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16873     UnmapGadget(game_gadget[i]);
16874 }
16875
16876 void RedrawGameButtons()
16877 {
16878   int i;
16879
16880   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16881     RedrawGadget(game_gadget[i]);
16882 }
16883
16884 static void HandleGameButtonsExt(int id)
16885 {
16886   if (game_status != GAME_MODE_PLAYING)
16887     return;
16888
16889   switch (id)
16890   {
16891     case GAME_CTRL_ID_STOP:
16892       if (tape.playing)
16893         TapeStop();
16894       else
16895         RequestQuitGame(TRUE);
16896       break;
16897
16898     case GAME_CTRL_ID_PAUSE:
16899       if (options.network)
16900       {
16901 #if defined(NETWORK_AVALIABLE)
16902         if (tape.pausing)
16903           SendToServer_ContinuePlaying();
16904         else
16905           SendToServer_PausePlaying();
16906 #endif
16907       }
16908       else
16909         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16910       break;
16911
16912     case GAME_CTRL_ID_PLAY:
16913       if (tape.pausing)
16914       {
16915 #if defined(NETWORK_AVALIABLE)
16916         if (options.network)
16917           SendToServer_ContinuePlaying();
16918         else
16919 #endif
16920         {
16921           tape.pausing = FALSE;
16922           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16923         }
16924       }
16925       break;
16926
16927     case SOUND_CTRL_ID_MUSIC:
16928       if (setup.sound_music)
16929       { 
16930         setup.sound_music = FALSE;
16931
16932         FadeMusic();
16933       }
16934       else if (audio.music_available)
16935       { 
16936         setup.sound = setup.sound_music = TRUE;
16937
16938         SetAudioMode(setup.sound);
16939
16940         PlayLevelMusic();
16941       }
16942       break;
16943
16944     case SOUND_CTRL_ID_LOOPS:
16945       if (setup.sound_loops)
16946         setup.sound_loops = FALSE;
16947       else if (audio.loops_available)
16948       {
16949         setup.sound = setup.sound_loops = TRUE;
16950
16951         SetAudioMode(setup.sound);
16952       }
16953       break;
16954
16955     case SOUND_CTRL_ID_SIMPLE:
16956       if (setup.sound_simple)
16957         setup.sound_simple = FALSE;
16958       else if (audio.sound_available)
16959       {
16960         setup.sound = setup.sound_simple = TRUE;
16961
16962         SetAudioMode(setup.sound);
16963       }
16964       break;
16965
16966     default:
16967       break;
16968   }
16969 }
16970
16971 static void HandleGameButtons(struct GadgetInfo *gi)
16972 {
16973   HandleGameButtonsExt(gi->custom_id);
16974 }
16975
16976 void HandleSoundButtonKeys(Key key)
16977 {
16978 #if 1
16979   if (key == setup.shortcut.sound_simple)
16980     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16981   else if (key == setup.shortcut.sound_loops)
16982     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16983   else if (key == setup.shortcut.sound_music)
16984     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16985 #else
16986   if (key == setup.shortcut.sound_simple)
16987     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16988   else if (key == setup.shortcut.sound_loops)
16989     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16990   else if (key == setup.shortcut.sound_music)
16991     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16992 #endif
16993 }