rnd-20100707-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   if (EVEN(SCR_FIELDX))
4361   {
4362     SBX_Left--;
4363     // SBX_Right++;
4364   }
4365 #endif
4366 #endif
4367
4368   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4369     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4370
4371   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4372     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4373
4374   /* if local player not found, look for custom element that might create
4375      the player (make some assumptions about the right custom element) */
4376   if (!local_player->present)
4377   {
4378     int start_x = 0, start_y = 0;
4379     int found_rating = 0;
4380     int found_element = EL_UNDEFINED;
4381     int player_nr = local_player->index_nr;
4382
4383     SCAN_PLAYFIELD(x, y)
4384     {
4385       int element = Feld[x][y];
4386       int content;
4387       int xx, yy;
4388       boolean is_player;
4389
4390       if (level.use_start_element[player_nr] &&
4391           level.start_element[player_nr] == element &&
4392           found_rating < 4)
4393       {
4394         start_x = x;
4395         start_y = y;
4396
4397         found_rating = 4;
4398         found_element = element;
4399       }
4400
4401       if (!IS_CUSTOM_ELEMENT(element))
4402         continue;
4403
4404       if (CAN_CHANGE(element))
4405       {
4406         for (i = 0; i < element_info[element].num_change_pages; i++)
4407         {
4408           /* check for player created from custom element as single target */
4409           content = element_info[element].change_page[i].target_element;
4410           is_player = ELEM_IS_PLAYER(content);
4411
4412           if (is_player && (found_rating < 3 ||
4413                             (found_rating == 3 && element < found_element)))
4414           {
4415             start_x = x;
4416             start_y = y;
4417
4418             found_rating = 3;
4419             found_element = element;
4420           }
4421         }
4422       }
4423
4424       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4425       {
4426         /* check for player created from custom element as explosion content */
4427         content = element_info[element].content.e[xx][yy];
4428         is_player = ELEM_IS_PLAYER(content);
4429
4430         if (is_player && (found_rating < 2 ||
4431                           (found_rating == 2 && element < found_element)))
4432         {
4433           start_x = x + xx - 1;
4434           start_y = y + yy - 1;
4435
4436           found_rating = 2;
4437           found_element = element;
4438         }
4439
4440         if (!CAN_CHANGE(element))
4441           continue;
4442
4443         for (i = 0; i < element_info[element].num_change_pages; i++)
4444         {
4445           /* check for player created from custom element as extended target */
4446           content =
4447             element_info[element].change_page[i].target_content.e[xx][yy];
4448
4449           is_player = ELEM_IS_PLAYER(content);
4450
4451           if (is_player && (found_rating < 1 ||
4452                             (found_rating == 1 && element < found_element)))
4453           {
4454             start_x = x + xx - 1;
4455             start_y = y + yy - 1;
4456
4457             found_rating = 1;
4458             found_element = element;
4459           }
4460         }
4461       }
4462     }
4463
4464     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4465                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4466                 start_x - MIDPOSX);
4467
4468     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4469                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4470                 start_y - MIDPOSY);
4471   }
4472   else
4473   {
4474     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4475                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4476                 local_player->jx - MIDPOSX);
4477
4478     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4479                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4480                 local_player->jy - MIDPOSY);
4481   }
4482
4483 #if 0
4484   /* do not use PLAYING mask for fading out from main screen */
4485   game_status = GAME_MODE_MAIN;
4486 #endif
4487
4488   StopAnimation();
4489
4490   if (!game.restart_level)
4491     CloseDoor(DOOR_CLOSE_1);
4492
4493 #if 1
4494   if (level_editor_test_game)
4495     FadeSkipNextFadeIn();
4496   else
4497     FadeSetEnterScreen();
4498 #else
4499   if (level_editor_test_game)
4500     fading = fading_none;
4501   else
4502     fading = menu.destination;
4503 #endif
4504
4505 #if 1
4506   FadeOut(REDRAW_FIELD);
4507 #else
4508   if (do_fading)
4509     FadeOut(REDRAW_FIELD);
4510 #endif
4511
4512 #if 0
4513   game_status = GAME_MODE_PLAYING;
4514 #endif
4515
4516   /* !!! FIX THIS (START) !!! */
4517   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4518   {
4519     InitGameEngine_EM();
4520
4521     /* blit playfield from scroll buffer to normal back buffer for fading in */
4522     BlitScreenToBitmap_EM(backbuffer);
4523   }
4524   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4525   {
4526     InitGameEngine_SP();
4527
4528     /* blit playfield from scroll buffer to normal back buffer for fading in */
4529     BlitScreenToBitmap_SP(backbuffer);
4530   }
4531   else
4532   {
4533     DrawLevel();
4534     DrawAllPlayers();
4535
4536     /* after drawing the level, correct some elements */
4537     if (game.timegate_time_left == 0)
4538       CloseAllOpenTimegates();
4539
4540 #if NEW_TILESIZE
4541     BlitScreenToBitmap(backbuffer);
4542 #else
4543     /* blit playfield from scroll buffer to normal back buffer for fading in */
4544     if (setup.soft_scrolling)
4545       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4546 #endif
4547
4548     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4549   }
4550   /* !!! FIX THIS (END) !!! */
4551
4552 #if 1
4553   FadeIn(REDRAW_FIELD);
4554 #else
4555   if (do_fading)
4556     FadeIn(REDRAW_FIELD);
4557
4558   BackToFront();
4559 #endif
4560
4561   if (!game.restart_level)
4562   {
4563     /* copy default game door content to main double buffer */
4564 #if 1
4565 #if 1
4566     /* !!! CHECK AGAIN !!! */
4567     SetPanelBackground();
4568     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4569     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4570 #else
4571     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4572
4573     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4574     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4575     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4576                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4577 #endif
4578 #else
4579     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4580                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4581 #endif
4582   }
4583
4584   SetPanelBackground();
4585   SetDrawBackgroundMask(REDRAW_DOOR_1);
4586
4587 #if 1
4588   UpdateAndDisplayGameControlValues();
4589 #else
4590   UpdateGameDoorValues();
4591   DrawGameDoorValues();
4592 #endif
4593
4594   if (!game.restart_level)
4595   {
4596     UnmapGameButtons();
4597     UnmapTapeButtons();
4598     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4599     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4600     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4601     MapGameButtons();
4602     MapTapeButtons();
4603
4604     /* copy actual game door content to door double buffer for OpenDoor() */
4605     BlitBitmap(drawto, bitmap_db_door,
4606                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4607
4608     OpenDoor(DOOR_OPEN_ALL);
4609
4610     PlaySound(SND_GAME_STARTING);
4611
4612     if (setup.sound_music)
4613       PlayLevelMusic();
4614
4615     KeyboardAutoRepeatOffUnlessAutoplay();
4616
4617     if (options.debug)
4618     {
4619       for (i = 0; i < MAX_PLAYERS; i++)
4620         printf("Player %d %sactive.\n",
4621                i + 1, (stored_player[i].active ? "" : "not "));
4622     }
4623   }
4624
4625 #if 1
4626   UnmapAllGadgets();
4627
4628   MapGameButtons();
4629   MapTapeButtons();
4630 #endif
4631
4632   game.restart_level = FALSE;
4633 }
4634
4635 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4636 {
4637   /* this is used for non-R'n'D game engines to update certain engine values */
4638
4639   /* needed to determine if sounds are played within the visible screen area */
4640   scroll_x = actual_scroll_x;
4641   scroll_y = actual_scroll_y;
4642 }
4643
4644 void InitMovDir(int x, int y)
4645 {
4646   int i, element = Feld[x][y];
4647   static int xy[4][2] =
4648   {
4649     {  0, +1 },
4650     { +1,  0 },
4651     {  0, -1 },
4652     { -1,  0 }
4653   };
4654   static int direction[3][4] =
4655   {
4656     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4657     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4658     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4659   };
4660
4661   switch (element)
4662   {
4663     case EL_BUG_RIGHT:
4664     case EL_BUG_UP:
4665     case EL_BUG_LEFT:
4666     case EL_BUG_DOWN:
4667       Feld[x][y] = EL_BUG;
4668       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4669       break;
4670
4671     case EL_SPACESHIP_RIGHT:
4672     case EL_SPACESHIP_UP:
4673     case EL_SPACESHIP_LEFT:
4674     case EL_SPACESHIP_DOWN:
4675       Feld[x][y] = EL_SPACESHIP;
4676       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4677       break;
4678
4679     case EL_BD_BUTTERFLY_RIGHT:
4680     case EL_BD_BUTTERFLY_UP:
4681     case EL_BD_BUTTERFLY_LEFT:
4682     case EL_BD_BUTTERFLY_DOWN:
4683       Feld[x][y] = EL_BD_BUTTERFLY;
4684       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4685       break;
4686
4687     case EL_BD_FIREFLY_RIGHT:
4688     case EL_BD_FIREFLY_UP:
4689     case EL_BD_FIREFLY_LEFT:
4690     case EL_BD_FIREFLY_DOWN:
4691       Feld[x][y] = EL_BD_FIREFLY;
4692       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4693       break;
4694
4695     case EL_PACMAN_RIGHT:
4696     case EL_PACMAN_UP:
4697     case EL_PACMAN_LEFT:
4698     case EL_PACMAN_DOWN:
4699       Feld[x][y] = EL_PACMAN;
4700       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4701       break;
4702
4703     case EL_YAMYAM_LEFT:
4704     case EL_YAMYAM_RIGHT:
4705     case EL_YAMYAM_UP:
4706     case EL_YAMYAM_DOWN:
4707       Feld[x][y] = EL_YAMYAM;
4708       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4709       break;
4710
4711     case EL_SP_SNIKSNAK:
4712       MovDir[x][y] = MV_UP;
4713       break;
4714
4715     case EL_SP_ELECTRON:
4716       MovDir[x][y] = MV_LEFT;
4717       break;
4718
4719     case EL_MOLE_LEFT:
4720     case EL_MOLE_RIGHT:
4721     case EL_MOLE_UP:
4722     case EL_MOLE_DOWN:
4723       Feld[x][y] = EL_MOLE;
4724       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4725       break;
4726
4727     default:
4728       if (IS_CUSTOM_ELEMENT(element))
4729       {
4730         struct ElementInfo *ei = &element_info[element];
4731         int move_direction_initial = ei->move_direction_initial;
4732         int move_pattern = ei->move_pattern;
4733
4734         if (move_direction_initial == MV_START_PREVIOUS)
4735         {
4736           if (MovDir[x][y] != MV_NONE)
4737             return;
4738
4739           move_direction_initial = MV_START_AUTOMATIC;
4740         }
4741
4742         if (move_direction_initial == MV_START_RANDOM)
4743           MovDir[x][y] = 1 << RND(4);
4744         else if (move_direction_initial & MV_ANY_DIRECTION)
4745           MovDir[x][y] = move_direction_initial;
4746         else if (move_pattern == MV_ALL_DIRECTIONS ||
4747                  move_pattern == MV_TURNING_LEFT ||
4748                  move_pattern == MV_TURNING_RIGHT ||
4749                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4750                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4751                  move_pattern == MV_TURNING_RANDOM)
4752           MovDir[x][y] = 1 << RND(4);
4753         else if (move_pattern == MV_HORIZONTAL)
4754           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4755         else if (move_pattern == MV_VERTICAL)
4756           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4757         else if (move_pattern & MV_ANY_DIRECTION)
4758           MovDir[x][y] = element_info[element].move_pattern;
4759         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4760                  move_pattern == MV_ALONG_RIGHT_SIDE)
4761         {
4762           /* use random direction as default start direction */
4763           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4764             MovDir[x][y] = 1 << RND(4);
4765
4766           for (i = 0; i < NUM_DIRECTIONS; i++)
4767           {
4768             int x1 = x + xy[i][0];
4769             int y1 = y + xy[i][1];
4770
4771             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4772             {
4773               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4774                 MovDir[x][y] = direction[0][i];
4775               else
4776                 MovDir[x][y] = direction[1][i];
4777
4778               break;
4779             }
4780           }
4781         }                
4782       }
4783       else
4784       {
4785         MovDir[x][y] = 1 << RND(4);
4786
4787         if (element != EL_BUG &&
4788             element != EL_SPACESHIP &&
4789             element != EL_BD_BUTTERFLY &&
4790             element != EL_BD_FIREFLY)
4791           break;
4792
4793         for (i = 0; i < NUM_DIRECTIONS; i++)
4794         {
4795           int x1 = x + xy[i][0];
4796           int y1 = y + xy[i][1];
4797
4798           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4799           {
4800             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4801             {
4802               MovDir[x][y] = direction[0][i];
4803               break;
4804             }
4805             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4806                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4807             {
4808               MovDir[x][y] = direction[1][i];
4809               break;
4810             }
4811           }
4812         }
4813       }
4814       break;
4815   }
4816
4817   GfxDir[x][y] = MovDir[x][y];
4818 }
4819
4820 void InitAmoebaNr(int x, int y)
4821 {
4822   int i;
4823   int group_nr = AmoebeNachbarNr(x, y);
4824
4825   if (group_nr == 0)
4826   {
4827     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4828     {
4829       if (AmoebaCnt[i] == 0)
4830       {
4831         group_nr = i;
4832         break;
4833       }
4834     }
4835   }
4836
4837   AmoebaNr[x][y] = group_nr;
4838   AmoebaCnt[group_nr]++;
4839   AmoebaCnt2[group_nr]++;
4840 }
4841
4842 static void PlayerWins(struct PlayerInfo *player)
4843 {
4844   player->LevelSolved = TRUE;
4845   player->GameOver = TRUE;
4846
4847   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4848                          level.native_em_level->lev->score : player->score);
4849
4850   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4851   player->LevelSolved_CountingScore = player->score_final;
4852 }
4853
4854 void GameWon()
4855 {
4856   static int time, time_final;
4857   static int score, score_final;
4858   static int game_over_delay_1 = 0;
4859   static int game_over_delay_2 = 0;
4860   int game_over_delay_value_1 = 50;
4861   int game_over_delay_value_2 = 50;
4862
4863   if (!local_player->LevelSolved_GameWon)
4864   {
4865     int i;
4866
4867     /* do not start end game actions before the player stops moving (to exit) */
4868     if (local_player->MovPos)
4869       return;
4870
4871     local_player->LevelSolved_GameWon = TRUE;
4872     local_player->LevelSolved_SaveTape = tape.recording;
4873     local_player->LevelSolved_SaveScore = !tape.playing;
4874
4875     if (tape.auto_play)         /* tape might already be stopped here */
4876       tape.auto_play_level_solved = TRUE;
4877
4878 #if 1
4879     TapeStop();
4880 #endif
4881
4882     game_over_delay_1 = game_over_delay_value_1;
4883     game_over_delay_2 = game_over_delay_value_2;
4884
4885     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4886     score = score_final = local_player->score_final;
4887
4888     if (TimeLeft > 0)
4889     {
4890       time_final = 0;
4891       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4892     }
4893     else if (level.time == 0 && TimePlayed < 999)
4894     {
4895       time_final = 999;
4896       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4897     }
4898
4899     local_player->score_final = score_final;
4900
4901     if (level_editor_test_game)
4902     {
4903       time = time_final;
4904       score = score_final;
4905
4906 #if 1
4907       local_player->LevelSolved_CountingTime = time;
4908       local_player->LevelSolved_CountingScore = score;
4909
4910       game_panel_controls[GAME_PANEL_TIME].value = time;
4911       game_panel_controls[GAME_PANEL_SCORE].value = score;
4912
4913       DisplayGameControlValues();
4914 #else
4915       DrawGameValue_Time(time);
4916       DrawGameValue_Score(score);
4917 #endif
4918     }
4919
4920     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4921     {
4922       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4923       {
4924         /* close exit door after last player */
4925         if ((AllPlayersGone &&
4926              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4927               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4928               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4929             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4930             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4931         {
4932           int element = Feld[ExitX][ExitY];
4933
4934 #if 0
4935           if (element == EL_EM_EXIT_OPEN ||
4936               element == EL_EM_STEEL_EXIT_OPEN)
4937           {
4938             Bang(ExitX, ExitY);
4939           }
4940           else
4941 #endif
4942           {
4943             Feld[ExitX][ExitY] =
4944               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4945                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4946                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4947                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4948                EL_EM_STEEL_EXIT_CLOSING);
4949
4950             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4951           }
4952         }
4953
4954         /* player disappears */
4955         DrawLevelField(ExitX, ExitY);
4956       }
4957
4958       for (i = 0; i < MAX_PLAYERS; i++)
4959       {
4960         struct PlayerInfo *player = &stored_player[i];
4961
4962         if (player->present)
4963         {
4964           RemovePlayer(player);
4965
4966           /* player disappears */
4967           DrawLevelField(player->jx, player->jy);
4968         }
4969       }
4970     }
4971
4972     PlaySound(SND_GAME_WINNING);
4973   }
4974
4975   if (game_over_delay_1 > 0)
4976   {
4977     game_over_delay_1--;
4978
4979     return;
4980   }
4981
4982   if (time != time_final)
4983   {
4984     int time_to_go = ABS(time_final - time);
4985     int time_count_dir = (time < time_final ? +1 : -1);
4986     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4987
4988     time  += time_count_steps * time_count_dir;
4989     score += time_count_steps * level.score[SC_TIME_BONUS];
4990
4991 #if 1
4992     local_player->LevelSolved_CountingTime = time;
4993     local_player->LevelSolved_CountingScore = score;
4994
4995     game_panel_controls[GAME_PANEL_TIME].value = time;
4996     game_panel_controls[GAME_PANEL_SCORE].value = score;
4997
4998     DisplayGameControlValues();
4999 #else
5000     DrawGameValue_Time(time);
5001     DrawGameValue_Score(score);
5002 #endif
5003
5004     if (time == time_final)
5005       StopSound(SND_GAME_LEVELTIME_BONUS);
5006     else if (setup.sound_loops)
5007       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5008     else
5009       PlaySound(SND_GAME_LEVELTIME_BONUS);
5010
5011     return;
5012   }
5013
5014   local_player->LevelSolved_PanelOff = TRUE;
5015
5016   if (game_over_delay_2 > 0)
5017   {
5018     game_over_delay_2--;
5019
5020     return;
5021   }
5022
5023 #if 1
5024   GameEnd();
5025 #endif
5026 }
5027
5028 void GameEnd()
5029 {
5030   int hi_pos;
5031   boolean raise_level = FALSE;
5032
5033   local_player->LevelSolved_GameEnd = TRUE;
5034
5035   CloseDoor(DOOR_CLOSE_1);
5036
5037   if (local_player->LevelSolved_SaveTape)
5038   {
5039 #if 0
5040     TapeStop();
5041 #endif
5042
5043 #if 1
5044     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5045 #else
5046     SaveTape(tape.level_nr);            /* ask to save tape */
5047 #endif
5048   }
5049
5050   if (level_editor_test_game)
5051   {
5052     game_status = GAME_MODE_MAIN;
5053
5054 #if 1
5055     DrawAndFadeInMainMenu(REDRAW_FIELD);
5056 #else
5057     DrawMainMenu();
5058 #endif
5059
5060     return;
5061   }
5062
5063   if (!local_player->LevelSolved_SaveScore)
5064   {
5065 #if 1
5066     FadeOut(REDRAW_FIELD);
5067 #endif
5068
5069     game_status = GAME_MODE_MAIN;
5070
5071     DrawAndFadeInMainMenu(REDRAW_FIELD);
5072
5073     return;
5074   }
5075
5076   if (level_nr == leveldir_current->handicap_level)
5077   {
5078     leveldir_current->handicap_level++;
5079     SaveLevelSetup_SeriesInfo();
5080   }
5081
5082   if (level_nr < leveldir_current->last_level)
5083     raise_level = TRUE;                 /* advance to next level */
5084
5085   if ((hi_pos = NewHiScore()) >= 0) 
5086   {
5087     game_status = GAME_MODE_SCORES;
5088
5089     DrawHallOfFame(hi_pos);
5090
5091     if (raise_level)
5092     {
5093       level_nr++;
5094       TapeErase();
5095     }
5096   }
5097   else
5098   {
5099 #if 1
5100     FadeOut(REDRAW_FIELD);
5101 #endif
5102
5103     game_status = GAME_MODE_MAIN;
5104
5105     if (raise_level)
5106     {
5107       level_nr++;
5108       TapeErase();
5109     }
5110
5111     DrawAndFadeInMainMenu(REDRAW_FIELD);
5112   }
5113 }
5114
5115 int NewHiScore()
5116 {
5117   int k, l;
5118   int position = -1;
5119
5120   LoadScore(level_nr);
5121
5122   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5123       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5124     return -1;
5125
5126   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5127   {
5128     if (local_player->score_final > highscore[k].Score)
5129     {
5130       /* player has made it to the hall of fame */
5131
5132       if (k < MAX_SCORE_ENTRIES - 1)
5133       {
5134         int m = MAX_SCORE_ENTRIES - 1;
5135
5136 #ifdef ONE_PER_NAME
5137         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5138           if (strEqual(setup.player_name, highscore[l].Name))
5139             m = l;
5140         if (m == k)     /* player's new highscore overwrites his old one */
5141           goto put_into_list;
5142 #endif
5143
5144         for (l = m; l > k; l--)
5145         {
5146           strcpy(highscore[l].Name, highscore[l - 1].Name);
5147           highscore[l].Score = highscore[l - 1].Score;
5148         }
5149       }
5150
5151 #ifdef ONE_PER_NAME
5152       put_into_list:
5153 #endif
5154       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5155       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5156       highscore[k].Score = local_player->score_final; 
5157       position = k;
5158       break;
5159     }
5160
5161 #ifdef ONE_PER_NAME
5162     else if (!strncmp(setup.player_name, highscore[k].Name,
5163                       MAX_PLAYER_NAME_LEN))
5164       break;    /* player already there with a higher score */
5165 #endif
5166
5167   }
5168
5169   if (position >= 0) 
5170     SaveScore(level_nr);
5171
5172   return position;
5173 }
5174
5175 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5176 {
5177   int element = Feld[x][y];
5178   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5179   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5180   int horiz_move = (dx != 0);
5181   int sign = (horiz_move ? dx : dy);
5182   int step = sign * element_info[element].move_stepsize;
5183
5184   /* special values for move stepsize for spring and things on conveyor belt */
5185   if (horiz_move)
5186   {
5187     if (CAN_FALL(element) &&
5188         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5189       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5190     else if (element == EL_SPRING)
5191       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5192   }
5193
5194   return step;
5195 }
5196
5197 inline static int getElementMoveStepsize(int x, int y)
5198 {
5199   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5200 }
5201
5202 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5203 {
5204   if (player->GfxAction != action || player->GfxDir != dir)
5205   {
5206 #if 0
5207     printf("Player frame reset! (%d => %d, %d => %d)\n",
5208            player->GfxAction, action, player->GfxDir, dir);
5209 #endif
5210
5211     player->GfxAction = action;
5212     player->GfxDir = dir;
5213     player->Frame = 0;
5214     player->StepFrame = 0;
5215   }
5216 }
5217
5218 #if USE_GFX_RESET_GFX_ANIMATION
5219 static void ResetGfxFrame(int x, int y, boolean redraw)
5220 {
5221   int element = Feld[x][y];
5222   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5223   int last_gfx_frame = GfxFrame[x][y];
5224
5225   if (graphic_info[graphic].anim_global_sync)
5226     GfxFrame[x][y] = FrameCounter;
5227   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5228     GfxFrame[x][y] = CustomValue[x][y];
5229   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5230     GfxFrame[x][y] = element_info[element].collect_score;
5231   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5232     GfxFrame[x][y] = ChangeDelay[x][y];
5233
5234   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5235     DrawLevelGraphicAnimation(x, y, graphic);
5236 }
5237 #endif
5238
5239 static void ResetGfxAnimation(int x, int y)
5240 {
5241   GfxAction[x][y] = ACTION_DEFAULT;
5242   GfxDir[x][y] = MovDir[x][y];
5243   GfxFrame[x][y] = 0;
5244
5245 #if USE_GFX_RESET_GFX_ANIMATION
5246   ResetGfxFrame(x, y, FALSE);
5247 #endif
5248 }
5249
5250 static void ResetRandomAnimationValue(int x, int y)
5251 {
5252   GfxRandom[x][y] = INIT_GFX_RANDOM();
5253 }
5254
5255 void InitMovingField(int x, int y, int direction)
5256 {
5257   int element = Feld[x][y];
5258   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5259   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5260   int newx = x + dx;
5261   int newy = y + dy;
5262   boolean is_moving_before, is_moving_after;
5263 #if 0
5264   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5265 #endif
5266
5267   /* check if element was/is moving or being moved before/after mode change */
5268 #if 1
5269 #if 1
5270   is_moving_before = (WasJustMoving[x][y] != 0);
5271 #else
5272   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5273   is_moving_before = WasJustMoving[x][y];
5274 #endif
5275 #else
5276   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5277 #endif
5278   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5279
5280   /* reset animation only for moving elements which change direction of moving
5281      or which just started or stopped moving
5282      (else CEs with property "can move" / "not moving" are reset each frame) */
5283 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5284 #if 1
5285   if (is_moving_before != is_moving_after ||
5286       direction != MovDir[x][y])
5287     ResetGfxAnimation(x, y);
5288 #else
5289   if ((is_moving_before || is_moving_after) && !continues_moving)
5290     ResetGfxAnimation(x, y);
5291 #endif
5292 #else
5293   if (!continues_moving)
5294     ResetGfxAnimation(x, y);
5295 #endif
5296
5297   MovDir[x][y] = direction;
5298   GfxDir[x][y] = direction;
5299
5300 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5301   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5302                      direction == MV_DOWN && CAN_FALL(element) ?
5303                      ACTION_FALLING : ACTION_MOVING);
5304 #else
5305   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5306                      ACTION_FALLING : ACTION_MOVING);
5307 #endif
5308
5309   /* this is needed for CEs with property "can move" / "not moving" */
5310
5311   if (is_moving_after)
5312   {
5313     if (Feld[newx][newy] == EL_EMPTY)
5314       Feld[newx][newy] = EL_BLOCKED;
5315
5316     MovDir[newx][newy] = MovDir[x][y];
5317
5318 #if USE_NEW_CUSTOM_VALUE
5319     CustomValue[newx][newy] = CustomValue[x][y];
5320 #endif
5321
5322     GfxFrame[newx][newy] = GfxFrame[x][y];
5323     GfxRandom[newx][newy] = GfxRandom[x][y];
5324     GfxAction[newx][newy] = GfxAction[x][y];
5325     GfxDir[newx][newy] = GfxDir[x][y];
5326   }
5327 }
5328
5329 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5330 {
5331   int direction = MovDir[x][y];
5332   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5333   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5334
5335   *goes_to_x = newx;
5336   *goes_to_y = newy;
5337 }
5338
5339 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5340 {
5341   int oldx = x, oldy = y;
5342   int direction = MovDir[x][y];
5343
5344   if (direction == MV_LEFT)
5345     oldx++;
5346   else if (direction == MV_RIGHT)
5347     oldx--;
5348   else if (direction == MV_UP)
5349     oldy++;
5350   else if (direction == MV_DOWN)
5351     oldy--;
5352
5353   *comes_from_x = oldx;
5354   *comes_from_y = oldy;
5355 }
5356
5357 int MovingOrBlocked2Element(int x, int y)
5358 {
5359   int element = Feld[x][y];
5360
5361   if (element == EL_BLOCKED)
5362   {
5363     int oldx, oldy;
5364
5365     Blocked2Moving(x, y, &oldx, &oldy);
5366     return Feld[oldx][oldy];
5367   }
5368   else
5369     return element;
5370 }
5371
5372 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5373 {
5374   /* like MovingOrBlocked2Element(), but if element is moving
5375      and (x,y) is the field the moving element is just leaving,
5376      return EL_BLOCKED instead of the element value */
5377   int element = Feld[x][y];
5378
5379   if (IS_MOVING(x, y))
5380   {
5381     if (element == EL_BLOCKED)
5382     {
5383       int oldx, oldy;
5384
5385       Blocked2Moving(x, y, &oldx, &oldy);
5386       return Feld[oldx][oldy];
5387     }
5388     else
5389       return EL_BLOCKED;
5390   }
5391   else
5392     return element;
5393 }
5394
5395 static void RemoveField(int x, int y)
5396 {
5397   Feld[x][y] = EL_EMPTY;
5398
5399   MovPos[x][y] = 0;
5400   MovDir[x][y] = 0;
5401   MovDelay[x][y] = 0;
5402
5403 #if USE_NEW_CUSTOM_VALUE
5404   CustomValue[x][y] = 0;
5405 #endif
5406
5407   AmoebaNr[x][y] = 0;
5408   ChangeDelay[x][y] = 0;
5409   ChangePage[x][y] = -1;
5410   Pushed[x][y] = FALSE;
5411
5412 #if 0
5413   ExplodeField[x][y] = EX_TYPE_NONE;
5414 #endif
5415
5416   GfxElement[x][y] = EL_UNDEFINED;
5417   GfxAction[x][y] = ACTION_DEFAULT;
5418   GfxDir[x][y] = MV_NONE;
5419 #if 0
5420   /* !!! this would prevent the removed tile from being redrawn !!! */
5421   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5422 #endif
5423 }
5424
5425 void RemoveMovingField(int x, int y)
5426 {
5427   int oldx = x, oldy = y, newx = x, newy = y;
5428   int element = Feld[x][y];
5429   int next_element = EL_UNDEFINED;
5430
5431   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5432     return;
5433
5434   if (IS_MOVING(x, y))
5435   {
5436     Moving2Blocked(x, y, &newx, &newy);
5437
5438     if (Feld[newx][newy] != EL_BLOCKED)
5439     {
5440       /* element is moving, but target field is not free (blocked), but
5441          already occupied by something different (example: acid pool);
5442          in this case, only remove the moving field, but not the target */
5443
5444       RemoveField(oldx, oldy);
5445
5446       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5447
5448       TEST_DrawLevelField(oldx, oldy);
5449
5450       return;
5451     }
5452   }
5453   else if (element == EL_BLOCKED)
5454   {
5455     Blocked2Moving(x, y, &oldx, &oldy);
5456     if (!IS_MOVING(oldx, oldy))
5457       return;
5458   }
5459
5460   if (element == EL_BLOCKED &&
5461       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5462        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5463        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5464        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5465        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5466        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5467     next_element = get_next_element(Feld[oldx][oldy]);
5468
5469   RemoveField(oldx, oldy);
5470   RemoveField(newx, newy);
5471
5472   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5473
5474   if (next_element != EL_UNDEFINED)
5475     Feld[oldx][oldy] = next_element;
5476
5477   TEST_DrawLevelField(oldx, oldy);
5478   TEST_DrawLevelField(newx, newy);
5479 }
5480
5481 void DrawDynamite(int x, int y)
5482 {
5483   int sx = SCREENX(x), sy = SCREENY(y);
5484   int graphic = el2img(Feld[x][y]);
5485   int frame;
5486
5487   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5488     return;
5489
5490   if (IS_WALKABLE_INSIDE(Back[x][y]))
5491     return;
5492
5493   if (Back[x][y])
5494     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5495   else if (Store[x][y])
5496     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5497
5498   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5499
5500   if (Back[x][y] || Store[x][y])
5501     DrawGraphicThruMask(sx, sy, graphic, frame);
5502   else
5503     DrawGraphic(sx, sy, graphic, frame);
5504 }
5505
5506 void CheckDynamite(int x, int y)
5507 {
5508   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5509   {
5510     MovDelay[x][y]--;
5511
5512     if (MovDelay[x][y] != 0)
5513     {
5514       DrawDynamite(x, y);
5515       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5516
5517       return;
5518     }
5519   }
5520
5521   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5522
5523   Bang(x, y);
5524 }
5525
5526 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5527 {
5528   boolean num_checked_players = 0;
5529   int i;
5530
5531   for (i = 0; i < MAX_PLAYERS; i++)
5532   {
5533     if (stored_player[i].active)
5534     {
5535       int sx = stored_player[i].jx;
5536       int sy = stored_player[i].jy;
5537
5538       if (num_checked_players == 0)
5539       {
5540         *sx1 = *sx2 = sx;
5541         *sy1 = *sy2 = sy;
5542       }
5543       else
5544       {
5545         *sx1 = MIN(*sx1, sx);
5546         *sy1 = MIN(*sy1, sy);
5547         *sx2 = MAX(*sx2, sx);
5548         *sy2 = MAX(*sy2, sy);
5549       }
5550
5551       num_checked_players++;
5552     }
5553   }
5554 }
5555
5556 static boolean checkIfAllPlayersFitToScreen_RND()
5557 {
5558   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5559
5560   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5561
5562   return (sx2 - sx1 < SCR_FIELDX &&
5563           sy2 - sy1 < SCR_FIELDY);
5564 }
5565
5566 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5567 {
5568   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5569
5570   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5571
5572   *sx = (sx1 + sx2) / 2;
5573   *sy = (sy1 + sy2) / 2;
5574 }
5575
5576 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5577                         boolean center_screen, boolean quick_relocation)
5578 {
5579   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5580   boolean no_delay = (tape.warp_forward);
5581   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5582   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5583
5584   if (quick_relocation)
5585   {
5586     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5587     {
5588       if (!level.shifted_relocation || center_screen)
5589       {
5590         /* quick relocation (without scrolling), with centering of screen */
5591
5592         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5593                     x > SBX_Right + MIDPOSX ? SBX_Right :
5594                     x - MIDPOSX);
5595
5596         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5597                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5598                     y - MIDPOSY);
5599       }
5600       else
5601       {
5602         /* quick relocation (without scrolling), but do not center screen */
5603
5604         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5605                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5606                                old_x - MIDPOSX);
5607
5608         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5609                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5610                                old_y - MIDPOSY);
5611
5612         int offset_x = x + (scroll_x - center_scroll_x);
5613         int offset_y = y + (scroll_y - center_scroll_y);
5614
5615         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5616                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5617                     offset_x - MIDPOSX);
5618
5619         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5620                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5621                     offset_y - MIDPOSY);
5622       }
5623     }
5624     else
5625     {
5626 #if 1
5627       if (!level.shifted_relocation || center_screen)
5628       {
5629         /* quick relocation (without scrolling), with centering of screen */
5630
5631         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5632                     x > SBX_Right + MIDPOSX ? SBX_Right :
5633                     x - MIDPOSX);
5634
5635         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5636                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5637                     y - MIDPOSY);
5638       }
5639       else
5640       {
5641         /* quick relocation (without scrolling), but do not center screen */
5642
5643         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5644                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5645                                old_x - MIDPOSX);
5646
5647         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5648                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5649                                old_y - MIDPOSY);
5650
5651         int offset_x = x + (scroll_x - center_scroll_x);
5652         int offset_y = y + (scroll_y - center_scroll_y);
5653
5654         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5655                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5656                     offset_x - MIDPOSX);
5657
5658         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5659                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5660                     offset_y - MIDPOSY);
5661       }
5662 #else
5663       /* quick relocation (without scrolling), inside visible screen area */
5664
5665       int offset = game.scroll_delay_value;
5666
5667       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5668           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5669         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5670
5671       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5672           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5673         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5674
5675       /* don't scroll over playfield boundaries */
5676       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5677         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5678
5679       /* don't scroll over playfield boundaries */
5680       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5681         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5682 #endif
5683     }
5684
5685     RedrawPlayfield(TRUE, 0,0,0,0);
5686   }
5687   else
5688   {
5689 #if 1
5690     int scroll_xx, scroll_yy;
5691
5692     if (!level.shifted_relocation || center_screen)
5693     {
5694       /* visible relocation (with scrolling), with centering of screen */
5695
5696       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5697                    x > SBX_Right + MIDPOSX ? SBX_Right :
5698                    x - MIDPOSX);
5699
5700       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5701                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5702                    y - MIDPOSY);
5703     }
5704     else
5705     {
5706       /* visible relocation (with scrolling), but do not center screen */
5707
5708       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5709                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5710                              old_x - MIDPOSX);
5711
5712       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5713                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5714                              old_y - MIDPOSY);
5715
5716       int offset_x = x + (scroll_x - center_scroll_x);
5717       int offset_y = y + (scroll_y - center_scroll_y);
5718
5719       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5720                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5721                    offset_x - MIDPOSX);
5722
5723       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5724                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5725                    offset_y - MIDPOSY);
5726     }
5727
5728 #else
5729
5730     /* visible relocation (with scrolling), with centering of screen */
5731
5732     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5733                      x > SBX_Right + MIDPOSX ? SBX_Right :
5734                      x - MIDPOSX);
5735
5736     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5737                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5738                      y - MIDPOSY);
5739 #endif
5740
5741     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5742
5743     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5744     {
5745       int dx = 0, dy = 0;
5746       int fx = FX, fy = FY;
5747
5748       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5749       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5750
5751       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5752         break;
5753
5754       scroll_x -= dx;
5755       scroll_y -= dy;
5756
5757       fx += dx * TILEX / 2;
5758       fy += dy * TILEY / 2;
5759
5760       ScrollLevel(dx, dy);
5761       DrawAllPlayers();
5762
5763       /* scroll in two steps of half tile size to make things smoother */
5764       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5765       FlushDisplay();
5766       Delay(wait_delay_value);
5767
5768       /* scroll second step to align at full tile size */
5769       BackToFront();
5770       Delay(wait_delay_value);
5771     }
5772
5773     DrawAllPlayers();
5774     BackToFront();
5775     Delay(wait_delay_value);
5776   }
5777 }
5778
5779 void RelocatePlayer(int jx, int jy, int el_player_raw)
5780 {
5781   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5782   int player_nr = GET_PLAYER_NR(el_player);
5783   struct PlayerInfo *player = &stored_player[player_nr];
5784   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5785   boolean no_delay = (tape.warp_forward);
5786   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5787   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5788   int old_jx = player->jx;
5789   int old_jy = player->jy;
5790   int old_element = Feld[old_jx][old_jy];
5791   int element = Feld[jx][jy];
5792   boolean player_relocated = (old_jx != jx || old_jy != jy);
5793
5794   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5795   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5796   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5797   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5798   int leave_side_horiz = move_dir_horiz;
5799   int leave_side_vert  = move_dir_vert;
5800   int enter_side = enter_side_horiz | enter_side_vert;
5801   int leave_side = leave_side_horiz | leave_side_vert;
5802
5803   if (player->GameOver)         /* do not reanimate dead player */
5804     return;
5805
5806   if (!player_relocated)        /* no need to relocate the player */
5807     return;
5808
5809   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5810   {
5811     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5812     DrawLevelField(jx, jy);
5813   }
5814
5815   if (player->present)
5816   {
5817     while (player->MovPos)
5818     {
5819       ScrollPlayer(player, SCROLL_GO_ON);
5820       ScrollScreen(NULL, SCROLL_GO_ON);
5821
5822       AdvanceFrameAndPlayerCounters(player->index_nr);
5823
5824       DrawPlayer(player);
5825
5826       BackToFront();
5827       Delay(wait_delay_value);
5828     }
5829
5830     DrawPlayer(player);         /* needed here only to cleanup last field */
5831     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5832
5833     player->is_moving = FALSE;
5834   }
5835
5836   if (IS_CUSTOM_ELEMENT(old_element))
5837     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5838                                CE_LEFT_BY_PLAYER,
5839                                player->index_bit, leave_side);
5840
5841   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5842                                       CE_PLAYER_LEAVES_X,
5843                                       player->index_bit, leave_side);
5844
5845   Feld[jx][jy] = el_player;
5846   InitPlayerField(jx, jy, el_player, TRUE);
5847
5848   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5849      possible that the relocation target field did not contain a player element,
5850      but a walkable element, to which the new player was relocated -- in this
5851      case, restore that (already initialized!) element on the player field */
5852   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5853   {
5854     Feld[jx][jy] = element;     /* restore previously existing element */
5855 #if 0
5856     /* !!! do not initialize already initialized element a second time !!! */
5857     /* (this causes at least problems with "element creation" CE trigger for
5858        already existing elements, and existing Sokoban fields counted twice) */
5859     InitField(jx, jy, FALSE);
5860 #endif
5861   }
5862
5863   /* only visually relocate centered player */
5864   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5865                      FALSE, level.instant_relocation);
5866
5867   TestIfPlayerTouchesBadThing(jx, jy);
5868   TestIfPlayerTouchesCustomElement(jx, jy);
5869
5870   if (IS_CUSTOM_ELEMENT(element))
5871     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5872                                player->index_bit, enter_side);
5873
5874   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5875                                       player->index_bit, enter_side);
5876
5877 #if 1
5878   if (player->is_switching)
5879   {
5880     /* ensure that relocation while still switching an element does not cause
5881        a new element to be treated as also switched directly after relocation
5882        (this is important for teleporter switches that teleport the player to
5883        a place where another teleporter switch is in the same direction, which
5884        would then incorrectly be treated as immediately switched before the
5885        direction key that caused the switch was released) */
5886
5887     player->switch_x += jx - old_jx;
5888     player->switch_y += jy - old_jy;
5889   }
5890 #endif
5891 }
5892
5893 void Explode(int ex, int ey, int phase, int mode)
5894 {
5895   int x, y;
5896   int last_phase;
5897   int border_element;
5898
5899   /* !!! eliminate this variable !!! */
5900   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5901
5902   if (game.explosions_delayed)
5903   {
5904     ExplodeField[ex][ey] = mode;
5905     return;
5906   }
5907
5908   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5909   {
5910     int center_element = Feld[ex][ey];
5911     int artwork_element, explosion_element;     /* set these values later */
5912
5913 #if 0
5914     /* --- This is only really needed (and now handled) in "Impact()". --- */
5915     /* do not explode moving elements that left the explode field in time */
5916     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5917         center_element == EL_EMPTY &&
5918         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5919       return;
5920 #endif
5921
5922 #if 0
5923     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5924     if (mode == EX_TYPE_NORMAL ||
5925         mode == EX_TYPE_CENTER ||
5926         mode == EX_TYPE_CROSS)
5927       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5928 #endif
5929
5930     /* remove things displayed in background while burning dynamite */
5931     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5932       Back[ex][ey] = 0;
5933
5934     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5935     {
5936       /* put moving element to center field (and let it explode there) */
5937       center_element = MovingOrBlocked2Element(ex, ey);
5938       RemoveMovingField(ex, ey);
5939       Feld[ex][ey] = center_element;
5940     }
5941
5942     /* now "center_element" is finally determined -- set related values now */
5943     artwork_element = center_element;           /* for custom player artwork */
5944     explosion_element = center_element;         /* for custom player artwork */
5945
5946     if (IS_PLAYER(ex, ey))
5947     {
5948       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5949
5950       artwork_element = stored_player[player_nr].artwork_element;
5951
5952       if (level.use_explosion_element[player_nr])
5953       {
5954         explosion_element = level.explosion_element[player_nr];
5955         artwork_element = explosion_element;
5956       }
5957     }
5958
5959 #if 1
5960     if (mode == EX_TYPE_NORMAL ||
5961         mode == EX_TYPE_CENTER ||
5962         mode == EX_TYPE_CROSS)
5963       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5964 #endif
5965
5966     last_phase = element_info[explosion_element].explosion_delay + 1;
5967
5968     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5969     {
5970       int xx = x - ex + 1;
5971       int yy = y - ey + 1;
5972       int element;
5973
5974       if (!IN_LEV_FIELD(x, y) ||
5975           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5976           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5977         continue;
5978
5979       element = Feld[x][y];
5980
5981       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5982       {
5983         element = MovingOrBlocked2Element(x, y);
5984
5985         if (!IS_EXPLOSION_PROOF(element))
5986           RemoveMovingField(x, y);
5987       }
5988
5989       /* indestructible elements can only explode in center (but not flames) */
5990       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5991                                            mode == EX_TYPE_BORDER)) ||
5992           element == EL_FLAMES)
5993         continue;
5994
5995       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5996          behaviour, for example when touching a yamyam that explodes to rocks
5997          with active deadly shield, a rock is created under the player !!! */
5998       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5999 #if 0
6000       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6001           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6002            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6003 #else
6004       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6005 #endif
6006       {
6007         if (IS_ACTIVE_BOMB(element))
6008         {
6009           /* re-activate things under the bomb like gate or penguin */
6010           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6011           Back[x][y] = 0;
6012         }
6013
6014         continue;
6015       }
6016
6017       /* save walkable background elements while explosion on same tile */
6018       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6019           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6020         Back[x][y] = element;
6021
6022       /* ignite explodable elements reached by other explosion */
6023       if (element == EL_EXPLOSION)
6024         element = Store2[x][y];
6025
6026       if (AmoebaNr[x][y] &&
6027           (element == EL_AMOEBA_FULL ||
6028            element == EL_BD_AMOEBA ||
6029            element == EL_AMOEBA_GROWING))
6030       {
6031         AmoebaCnt[AmoebaNr[x][y]]--;
6032         AmoebaCnt2[AmoebaNr[x][y]]--;
6033       }
6034
6035       RemoveField(x, y);
6036
6037       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6038       {
6039         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6040
6041         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6042
6043         if (PLAYERINFO(ex, ey)->use_murphy)
6044           Store[x][y] = EL_EMPTY;
6045       }
6046
6047       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6048          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6049       else if (ELEM_IS_PLAYER(center_element))
6050         Store[x][y] = EL_EMPTY;
6051       else if (center_element == EL_YAMYAM)
6052         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6053       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6054         Store[x][y] = element_info[center_element].content.e[xx][yy];
6055 #if 1
6056       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6057          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6058          otherwise) -- FIX THIS !!! */
6059       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6060         Store[x][y] = element_info[element].content.e[1][1];
6061 #else
6062       else if (!CAN_EXPLODE(element))
6063         Store[x][y] = element_info[element].content.e[1][1];
6064 #endif
6065       else
6066         Store[x][y] = EL_EMPTY;
6067
6068       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6069           center_element == EL_AMOEBA_TO_DIAMOND)
6070         Store2[x][y] = element;
6071
6072       Feld[x][y] = EL_EXPLOSION;
6073       GfxElement[x][y] = artwork_element;
6074
6075       ExplodePhase[x][y] = 1;
6076       ExplodeDelay[x][y] = last_phase;
6077
6078       Stop[x][y] = TRUE;
6079     }
6080
6081     if (center_element == EL_YAMYAM)
6082       game.yamyam_content_nr =
6083         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6084
6085     return;
6086   }
6087
6088   if (Stop[ex][ey])
6089     return;
6090
6091   x = ex;
6092   y = ey;
6093
6094   if (phase == 1)
6095     GfxFrame[x][y] = 0;         /* restart explosion animation */
6096
6097   last_phase = ExplodeDelay[x][y];
6098
6099   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6100
6101 #ifdef DEBUG
6102
6103   /* activate this even in non-DEBUG version until cause for crash in
6104      getGraphicAnimationFrame() (see below) is found and eliminated */
6105
6106 #endif
6107 #if 1
6108
6109 #if 1
6110   /* this can happen if the player leaves an explosion just in time */
6111   if (GfxElement[x][y] == EL_UNDEFINED)
6112     GfxElement[x][y] = EL_EMPTY;
6113 #else
6114   if (GfxElement[x][y] == EL_UNDEFINED)
6115   {
6116     printf("\n\n");
6117     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6118     printf("Explode(): This should never happen!\n");
6119     printf("\n\n");
6120
6121     GfxElement[x][y] = EL_EMPTY;
6122   }
6123 #endif
6124
6125 #endif
6126
6127   border_element = Store2[x][y];
6128   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6129     border_element = StorePlayer[x][y];
6130
6131   if (phase == element_info[border_element].ignition_delay ||
6132       phase == last_phase)
6133   {
6134     boolean border_explosion = FALSE;
6135
6136     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6137         !PLAYER_EXPLOSION_PROTECTED(x, y))
6138     {
6139       KillPlayerUnlessExplosionProtected(x, y);
6140       border_explosion = TRUE;
6141     }
6142     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6143     {
6144       Feld[x][y] = Store2[x][y];
6145       Store2[x][y] = 0;
6146       Bang(x, y);
6147       border_explosion = TRUE;
6148     }
6149     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6150     {
6151       AmoebeUmwandeln(x, y);
6152       Store2[x][y] = 0;
6153       border_explosion = TRUE;
6154     }
6155
6156     /* if an element just explodes due to another explosion (chain-reaction),
6157        do not immediately end the new explosion when it was the last frame of
6158        the explosion (as it would be done in the following "if"-statement!) */
6159     if (border_explosion && phase == last_phase)
6160       return;
6161   }
6162
6163   if (phase == last_phase)
6164   {
6165     int element;
6166
6167     element = Feld[x][y] = Store[x][y];
6168     Store[x][y] = Store2[x][y] = 0;
6169     GfxElement[x][y] = EL_UNDEFINED;
6170
6171     /* player can escape from explosions and might therefore be still alive */
6172     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6173         element <= EL_PLAYER_IS_EXPLODING_4)
6174     {
6175       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6176       int explosion_element = EL_PLAYER_1 + player_nr;
6177       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6178       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6179
6180       if (level.use_explosion_element[player_nr])
6181         explosion_element = level.explosion_element[player_nr];
6182
6183       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6184                     element_info[explosion_element].content.e[xx][yy]);
6185     }
6186
6187     /* restore probably existing indestructible background element */
6188     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6189       element = Feld[x][y] = Back[x][y];
6190     Back[x][y] = 0;
6191
6192     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6193     GfxDir[x][y] = MV_NONE;
6194     ChangeDelay[x][y] = 0;
6195     ChangePage[x][y] = -1;
6196
6197 #if USE_NEW_CUSTOM_VALUE
6198     CustomValue[x][y] = 0;
6199 #endif
6200
6201     InitField_WithBug2(x, y, FALSE);
6202
6203     TEST_DrawLevelField(x, y);
6204
6205     TestIfElementTouchesCustomElement(x, y);
6206
6207     if (GFX_CRUMBLED(element))
6208       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6209
6210     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6211       StorePlayer[x][y] = 0;
6212
6213     if (ELEM_IS_PLAYER(element))
6214       RelocatePlayer(x, y, element);
6215   }
6216   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6217   {
6218     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6219     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6220
6221     if (phase == delay)
6222       TEST_DrawLevelFieldCrumbled(x, y);
6223
6224     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6225     {
6226       DrawLevelElement(x, y, Back[x][y]);
6227       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6228     }
6229     else if (IS_WALKABLE_UNDER(Back[x][y]))
6230     {
6231       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6232       DrawLevelElementThruMask(x, y, Back[x][y]);
6233     }
6234     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6235       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6236   }
6237 }
6238
6239 void DynaExplode(int ex, int ey)
6240 {
6241   int i, j;
6242   int dynabomb_element = Feld[ex][ey];
6243   int dynabomb_size = 1;
6244   boolean dynabomb_xl = FALSE;
6245   struct PlayerInfo *player;
6246   static int xy[4][2] =
6247   {
6248     { 0, -1 },
6249     { -1, 0 },
6250     { +1, 0 },
6251     { 0, +1 }
6252   };
6253
6254   if (IS_ACTIVE_BOMB(dynabomb_element))
6255   {
6256     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6257     dynabomb_size = player->dynabomb_size;
6258     dynabomb_xl = player->dynabomb_xl;
6259     player->dynabombs_left++;
6260   }
6261
6262   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6263
6264   for (i = 0; i < NUM_DIRECTIONS; i++)
6265   {
6266     for (j = 1; j <= dynabomb_size; j++)
6267     {
6268       int x = ex + j * xy[i][0];
6269       int y = ey + j * xy[i][1];
6270       int element;
6271
6272       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6273         break;
6274
6275       element = Feld[x][y];
6276
6277       /* do not restart explosions of fields with active bombs */
6278       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6279         continue;
6280
6281       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6282
6283       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6284           !IS_DIGGABLE(element) && !dynabomb_xl)
6285         break;
6286     }
6287   }
6288 }
6289
6290 void Bang(int x, int y)
6291 {
6292   int element = MovingOrBlocked2Element(x, y);
6293   int explosion_type = EX_TYPE_NORMAL;
6294
6295   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6296   {
6297     struct PlayerInfo *player = PLAYERINFO(x, y);
6298
6299 #if USE_FIX_CE_ACTION_WITH_PLAYER
6300     element = Feld[x][y] = player->initial_element;
6301 #else
6302     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6303                             player->element_nr);
6304 #endif
6305
6306     if (level.use_explosion_element[player->index_nr])
6307     {
6308       int explosion_element = level.explosion_element[player->index_nr];
6309
6310       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6311         explosion_type = EX_TYPE_CROSS;
6312       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6313         explosion_type = EX_TYPE_CENTER;
6314     }
6315   }
6316
6317   switch (element)
6318   {
6319     case EL_BUG:
6320     case EL_SPACESHIP:
6321     case EL_BD_BUTTERFLY:
6322     case EL_BD_FIREFLY:
6323     case EL_YAMYAM:
6324     case EL_DARK_YAMYAM:
6325     case EL_ROBOT:
6326     case EL_PACMAN:
6327     case EL_MOLE:
6328       RaiseScoreElement(element);
6329       break;
6330
6331     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6332     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6333     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6334     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6335     case EL_DYNABOMB_INCREASE_NUMBER:
6336     case EL_DYNABOMB_INCREASE_SIZE:
6337     case EL_DYNABOMB_INCREASE_POWER:
6338       explosion_type = EX_TYPE_DYNA;
6339       break;
6340
6341     case EL_DC_LANDMINE:
6342 #if 0
6343     case EL_EM_EXIT_OPEN:
6344     case EL_EM_STEEL_EXIT_OPEN:
6345 #endif
6346       explosion_type = EX_TYPE_CENTER;
6347       break;
6348
6349     case EL_PENGUIN:
6350     case EL_LAMP:
6351     case EL_LAMP_ACTIVE:
6352     case EL_AMOEBA_TO_DIAMOND:
6353       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6354         explosion_type = EX_TYPE_CENTER;
6355       break;
6356
6357     default:
6358       if (element_info[element].explosion_type == EXPLODES_CROSS)
6359         explosion_type = EX_TYPE_CROSS;
6360       else if (element_info[element].explosion_type == EXPLODES_1X1)
6361         explosion_type = EX_TYPE_CENTER;
6362       break;
6363   }
6364
6365   if (explosion_type == EX_TYPE_DYNA)
6366     DynaExplode(x, y);
6367   else
6368     Explode(x, y, EX_PHASE_START, explosion_type);
6369
6370   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6371 }
6372
6373 void SplashAcid(int x, int y)
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_LEFT;
6379
6380   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6381       (!IN_LEV_FIELD(x + 1, y - 2) ||
6382        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6383     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6384
6385   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6386 }
6387
6388 static void InitBeltMovement()
6389 {
6390   static int belt_base_element[4] =
6391   {
6392     EL_CONVEYOR_BELT_1_LEFT,
6393     EL_CONVEYOR_BELT_2_LEFT,
6394     EL_CONVEYOR_BELT_3_LEFT,
6395     EL_CONVEYOR_BELT_4_LEFT
6396   };
6397   static int belt_base_active_element[4] =
6398   {
6399     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6400     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6401     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6402     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6403   };
6404
6405   int x, y, i, j;
6406
6407   /* set frame order for belt animation graphic according to belt direction */
6408   for (i = 0; i < NUM_BELTS; i++)
6409   {
6410     int belt_nr = i;
6411
6412     for (j = 0; j < NUM_BELT_PARTS; j++)
6413     {
6414       int element = belt_base_active_element[belt_nr] + j;
6415       int graphic_1 = el2img(element);
6416       int graphic_2 = el2panelimg(element);
6417
6418       if (game.belt_dir[i] == MV_LEFT)
6419       {
6420         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6421         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6422       }
6423       else
6424       {
6425         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6426         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6427       }
6428     }
6429   }
6430
6431   SCAN_PLAYFIELD(x, y)
6432   {
6433     int element = Feld[x][y];
6434
6435     for (i = 0; i < NUM_BELTS; i++)
6436     {
6437       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6438       {
6439         int e_belt_nr = getBeltNrFromBeltElement(element);
6440         int belt_nr = i;
6441
6442         if (e_belt_nr == belt_nr)
6443         {
6444           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6445
6446           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6447         }
6448       }
6449     }
6450   }
6451 }
6452
6453 static void ToggleBeltSwitch(int x, int y)
6454 {
6455   static int belt_base_element[4] =
6456   {
6457     EL_CONVEYOR_BELT_1_LEFT,
6458     EL_CONVEYOR_BELT_2_LEFT,
6459     EL_CONVEYOR_BELT_3_LEFT,
6460     EL_CONVEYOR_BELT_4_LEFT
6461   };
6462   static int belt_base_active_element[4] =
6463   {
6464     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6465     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6466     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6467     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6468   };
6469   static int belt_base_switch_element[4] =
6470   {
6471     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6472     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6473     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6474     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6475   };
6476   static int belt_move_dir[4] =
6477   {
6478     MV_LEFT,
6479     MV_NONE,
6480     MV_RIGHT,
6481     MV_NONE,
6482   };
6483
6484   int element = Feld[x][y];
6485   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6486   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6487   int belt_dir = belt_move_dir[belt_dir_nr];
6488   int xx, yy, i;
6489
6490   if (!IS_BELT_SWITCH(element))
6491     return;
6492
6493   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6494   game.belt_dir[belt_nr] = belt_dir;
6495
6496   if (belt_dir_nr == 3)
6497     belt_dir_nr = 1;
6498
6499   /* set frame order for belt animation graphic according to belt direction */
6500   for (i = 0; i < NUM_BELT_PARTS; i++)
6501   {
6502     int element = belt_base_active_element[belt_nr] + i;
6503     int graphic_1 = el2img(element);
6504     int graphic_2 = el2panelimg(element);
6505
6506     if (belt_dir == MV_LEFT)
6507     {
6508       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6509       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6510     }
6511     else
6512     {
6513       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6514       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6515     }
6516   }
6517
6518   SCAN_PLAYFIELD(xx, yy)
6519   {
6520     int element = Feld[xx][yy];
6521
6522     if (IS_BELT_SWITCH(element))
6523     {
6524       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6525
6526       if (e_belt_nr == belt_nr)
6527       {
6528         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6529         TEST_DrawLevelField(xx, yy);
6530       }
6531     }
6532     else if (IS_BELT(element) && belt_dir != MV_NONE)
6533     {
6534       int e_belt_nr = getBeltNrFromBeltElement(element);
6535
6536       if (e_belt_nr == belt_nr)
6537       {
6538         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6539
6540         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6541         TEST_DrawLevelField(xx, yy);
6542       }
6543     }
6544     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6545     {
6546       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6547
6548       if (e_belt_nr == belt_nr)
6549       {
6550         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6551
6552         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6553         TEST_DrawLevelField(xx, yy);
6554       }
6555     }
6556   }
6557 }
6558
6559 static void ToggleSwitchgateSwitch(int x, int y)
6560 {
6561   int xx, yy;
6562
6563   game.switchgate_pos = !game.switchgate_pos;
6564
6565   SCAN_PLAYFIELD(xx, yy)
6566   {
6567     int element = Feld[xx][yy];
6568
6569 #if !USE_BOTH_SWITCHGATE_SWITCHES
6570     if (element == EL_SWITCHGATE_SWITCH_UP ||
6571         element == EL_SWITCHGATE_SWITCH_DOWN)
6572     {
6573       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6574       TEST_DrawLevelField(xx, yy);
6575     }
6576     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6577              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6578     {
6579       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6580       TEST_DrawLevelField(xx, yy);
6581     }
6582 #else
6583     if (element == EL_SWITCHGATE_SWITCH_UP)
6584     {
6585       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6586       TEST_DrawLevelField(xx, yy);
6587     }
6588     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6589     {
6590       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6591       TEST_DrawLevelField(xx, yy);
6592     }
6593     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6594     {
6595       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6596       TEST_DrawLevelField(xx, yy);
6597     }
6598     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6599     {
6600       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6601       TEST_DrawLevelField(xx, yy);
6602     }
6603 #endif
6604     else if (element == EL_SWITCHGATE_OPEN ||
6605              element == EL_SWITCHGATE_OPENING)
6606     {
6607       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6608
6609       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6610     }
6611     else if (element == EL_SWITCHGATE_CLOSED ||
6612              element == EL_SWITCHGATE_CLOSING)
6613     {
6614       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6615
6616       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6617     }
6618   }
6619 }
6620
6621 static int getInvisibleActiveFromInvisibleElement(int element)
6622 {
6623   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6624           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6625           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6626           element);
6627 }
6628
6629 static int getInvisibleFromInvisibleActiveElement(int element)
6630 {
6631   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6632           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6633           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6634           element);
6635 }
6636
6637 static void RedrawAllLightSwitchesAndInvisibleElements()
6638 {
6639   int x, y;
6640
6641   SCAN_PLAYFIELD(x, y)
6642   {
6643     int element = Feld[x][y];
6644
6645     if (element == EL_LIGHT_SWITCH &&
6646         game.light_time_left > 0)
6647     {
6648       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6649       TEST_DrawLevelField(x, y);
6650     }
6651     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6652              game.light_time_left == 0)
6653     {
6654       Feld[x][y] = EL_LIGHT_SWITCH;
6655       TEST_DrawLevelField(x, y);
6656     }
6657     else if (element == EL_EMC_DRIPPER &&
6658              game.light_time_left > 0)
6659     {
6660       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6661       TEST_DrawLevelField(x, y);
6662     }
6663     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6664              game.light_time_left == 0)
6665     {
6666       Feld[x][y] = EL_EMC_DRIPPER;
6667       TEST_DrawLevelField(x, y);
6668     }
6669     else if (element == EL_INVISIBLE_STEELWALL ||
6670              element == EL_INVISIBLE_WALL ||
6671              element == EL_INVISIBLE_SAND)
6672     {
6673       if (game.light_time_left > 0)
6674         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6675
6676       TEST_DrawLevelField(x, y);
6677
6678       /* uncrumble neighbour fields, if needed */
6679       if (element == EL_INVISIBLE_SAND)
6680         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6681     }
6682     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6683              element == EL_INVISIBLE_WALL_ACTIVE ||
6684              element == EL_INVISIBLE_SAND_ACTIVE)
6685     {
6686       if (game.light_time_left == 0)
6687         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6688
6689       TEST_DrawLevelField(x, y);
6690
6691       /* re-crumble neighbour fields, if needed */
6692       if (element == EL_INVISIBLE_SAND)
6693         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6694     }
6695   }
6696 }
6697
6698 static void RedrawAllInvisibleElementsForLenses()
6699 {
6700   int x, y;
6701
6702   SCAN_PLAYFIELD(x, y)
6703   {
6704     int element = Feld[x][y];
6705
6706     if (element == EL_EMC_DRIPPER &&
6707         game.lenses_time_left > 0)
6708     {
6709       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6710       TEST_DrawLevelField(x, y);
6711     }
6712     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6713              game.lenses_time_left == 0)
6714     {
6715       Feld[x][y] = EL_EMC_DRIPPER;
6716       TEST_DrawLevelField(x, y);
6717     }
6718     else if (element == EL_INVISIBLE_STEELWALL ||
6719              element == EL_INVISIBLE_WALL ||
6720              element == EL_INVISIBLE_SAND)
6721     {
6722       if (game.lenses_time_left > 0)
6723         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6724
6725       TEST_DrawLevelField(x, y);
6726
6727       /* uncrumble neighbour fields, if needed */
6728       if (element == EL_INVISIBLE_SAND)
6729         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6730     }
6731     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6732              element == EL_INVISIBLE_WALL_ACTIVE ||
6733              element == EL_INVISIBLE_SAND_ACTIVE)
6734     {
6735       if (game.lenses_time_left == 0)
6736         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6737
6738       TEST_DrawLevelField(x, y);
6739
6740       /* re-crumble neighbour fields, if needed */
6741       if (element == EL_INVISIBLE_SAND)
6742         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6743     }
6744   }
6745 }
6746
6747 static void RedrawAllInvisibleElementsForMagnifier()
6748 {
6749   int x, y;
6750
6751   SCAN_PLAYFIELD(x, y)
6752   {
6753     int element = Feld[x][y];
6754
6755     if (element == EL_EMC_FAKE_GRASS &&
6756         game.magnify_time_left > 0)
6757     {
6758       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6759       TEST_DrawLevelField(x, y);
6760     }
6761     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6762              game.magnify_time_left == 0)
6763     {
6764       Feld[x][y] = EL_EMC_FAKE_GRASS;
6765       TEST_DrawLevelField(x, y);
6766     }
6767     else if (IS_GATE_GRAY(element) &&
6768              game.magnify_time_left > 0)
6769     {
6770       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6771                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6772                     IS_EM_GATE_GRAY(element) ?
6773                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6774                     IS_EMC_GATE_GRAY(element) ?
6775                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6776                     IS_DC_GATE_GRAY(element) ?
6777                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6778                     element);
6779       TEST_DrawLevelField(x, y);
6780     }
6781     else if (IS_GATE_GRAY_ACTIVE(element) &&
6782              game.magnify_time_left == 0)
6783     {
6784       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6785                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6786                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6787                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6788                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6789                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6790                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6791                     EL_DC_GATE_WHITE_GRAY :
6792                     element);
6793       TEST_DrawLevelField(x, y);
6794     }
6795   }
6796 }
6797
6798 static void ToggleLightSwitch(int x, int y)
6799 {
6800   int element = Feld[x][y];
6801
6802   game.light_time_left =
6803     (element == EL_LIGHT_SWITCH ?
6804      level.time_light * FRAMES_PER_SECOND : 0);
6805
6806   RedrawAllLightSwitchesAndInvisibleElements();
6807 }
6808
6809 static void ActivateTimegateSwitch(int x, int y)
6810 {
6811   int xx, yy;
6812
6813   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6814
6815   SCAN_PLAYFIELD(xx, yy)
6816   {
6817     int element = Feld[xx][yy];
6818
6819     if (element == EL_TIMEGATE_CLOSED ||
6820         element == EL_TIMEGATE_CLOSING)
6821     {
6822       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6823       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6824     }
6825
6826     /*
6827     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6828     {
6829       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6830       TEST_DrawLevelField(xx, yy);
6831     }
6832     */
6833
6834   }
6835
6836 #if 1
6837   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6838                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6839 #else
6840   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6841 #endif
6842 }
6843
6844 void Impact(int x, int y)
6845 {
6846   boolean last_line = (y == lev_fieldy - 1);
6847   boolean object_hit = FALSE;
6848   boolean impact = (last_line || object_hit);
6849   int element = Feld[x][y];
6850   int smashed = EL_STEELWALL;
6851
6852   if (!last_line)       /* check if element below was hit */
6853   {
6854     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6855       return;
6856
6857     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6858                                          MovDir[x][y + 1] != MV_DOWN ||
6859                                          MovPos[x][y + 1] <= TILEY / 2));
6860
6861     /* do not smash moving elements that left the smashed field in time */
6862     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6863         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6864       object_hit = FALSE;
6865
6866 #if USE_QUICKSAND_IMPACT_BUGFIX
6867     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6868     {
6869       RemoveMovingField(x, y + 1);
6870       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6871       Feld[x][y + 2] = EL_ROCK;
6872       TEST_DrawLevelField(x, y + 2);
6873
6874       object_hit = TRUE;
6875     }
6876
6877     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6878     {
6879       RemoveMovingField(x, y + 1);
6880       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6881       Feld[x][y + 2] = EL_ROCK;
6882       TEST_DrawLevelField(x, y + 2);
6883
6884       object_hit = TRUE;
6885     }
6886 #endif
6887
6888     if (object_hit)
6889       smashed = MovingOrBlocked2Element(x, y + 1);
6890
6891     impact = (last_line || object_hit);
6892   }
6893
6894   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6895   {
6896     SplashAcid(x, y + 1);
6897     return;
6898   }
6899
6900   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6901   /* only reset graphic animation if graphic really changes after impact */
6902   if (impact &&
6903       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6904   {
6905     ResetGfxAnimation(x, y);
6906     TEST_DrawLevelField(x, y);
6907   }
6908
6909   if (impact && CAN_EXPLODE_IMPACT(element))
6910   {
6911     Bang(x, y);
6912     return;
6913   }
6914   else if (impact && element == EL_PEARL &&
6915            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6916   {
6917     ResetGfxAnimation(x, y);
6918
6919     Feld[x][y] = EL_PEARL_BREAKING;
6920     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6921     return;
6922   }
6923   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6924   {
6925     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6926
6927     return;
6928   }
6929
6930   if (impact && element == EL_AMOEBA_DROP)
6931   {
6932     if (object_hit && IS_PLAYER(x, y + 1))
6933       KillPlayerUnlessEnemyProtected(x, y + 1);
6934     else if (object_hit && smashed == EL_PENGUIN)
6935       Bang(x, y + 1);
6936     else
6937     {
6938       Feld[x][y] = EL_AMOEBA_GROWING;
6939       Store[x][y] = EL_AMOEBA_WET;
6940
6941       ResetRandomAnimationValue(x, y);
6942     }
6943     return;
6944   }
6945
6946   if (object_hit)               /* check which object was hit */
6947   {
6948     if ((CAN_PASS_MAGIC_WALL(element) && 
6949          (smashed == EL_MAGIC_WALL ||
6950           smashed == EL_BD_MAGIC_WALL)) ||
6951         (CAN_PASS_DC_MAGIC_WALL(element) &&
6952          smashed == EL_DC_MAGIC_WALL))
6953     {
6954       int xx, yy;
6955       int activated_magic_wall =
6956         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6957          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6958          EL_DC_MAGIC_WALL_ACTIVE);
6959
6960       /* activate magic wall / mill */
6961       SCAN_PLAYFIELD(xx, yy)
6962       {
6963         if (Feld[xx][yy] == smashed)
6964           Feld[xx][yy] = activated_magic_wall;
6965       }
6966
6967       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6968       game.magic_wall_active = TRUE;
6969
6970       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6971                             SND_MAGIC_WALL_ACTIVATING :
6972                             smashed == EL_BD_MAGIC_WALL ?
6973                             SND_BD_MAGIC_WALL_ACTIVATING :
6974                             SND_DC_MAGIC_WALL_ACTIVATING));
6975     }
6976
6977     if (IS_PLAYER(x, y + 1))
6978     {
6979       if (CAN_SMASH_PLAYER(element))
6980       {
6981         KillPlayerUnlessEnemyProtected(x, y + 1);
6982         return;
6983       }
6984     }
6985     else if (smashed == EL_PENGUIN)
6986     {
6987       if (CAN_SMASH_PLAYER(element))
6988       {
6989         Bang(x, y + 1);
6990         return;
6991       }
6992     }
6993     else if (element == EL_BD_DIAMOND)
6994     {
6995       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6996       {
6997         Bang(x, y + 1);
6998         return;
6999       }
7000     }
7001     else if (((element == EL_SP_INFOTRON ||
7002                element == EL_SP_ZONK) &&
7003               (smashed == EL_SP_SNIKSNAK ||
7004                smashed == EL_SP_ELECTRON ||
7005                smashed == EL_SP_DISK_ORANGE)) ||
7006              (element == EL_SP_INFOTRON &&
7007               smashed == EL_SP_DISK_YELLOW))
7008     {
7009       Bang(x, y + 1);
7010       return;
7011     }
7012     else if (CAN_SMASH_EVERYTHING(element))
7013     {
7014       if (IS_CLASSIC_ENEMY(smashed) ||
7015           CAN_EXPLODE_SMASHED(smashed))
7016       {
7017         Bang(x, y + 1);
7018         return;
7019       }
7020       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7021       {
7022         if (smashed == EL_LAMP ||
7023             smashed == EL_LAMP_ACTIVE)
7024         {
7025           Bang(x, y + 1);
7026           return;
7027         }
7028         else if (smashed == EL_NUT)
7029         {
7030           Feld[x][y + 1] = EL_NUT_BREAKING;
7031           PlayLevelSound(x, y, SND_NUT_BREAKING);
7032           RaiseScoreElement(EL_NUT);
7033           return;
7034         }
7035         else if (smashed == EL_PEARL)
7036         {
7037           ResetGfxAnimation(x, y);
7038
7039           Feld[x][y + 1] = EL_PEARL_BREAKING;
7040           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7041           return;
7042         }
7043         else if (smashed == EL_DIAMOND)
7044         {
7045           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7046           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7047           return;
7048         }
7049         else if (IS_BELT_SWITCH(smashed))
7050         {
7051           ToggleBeltSwitch(x, y + 1);
7052         }
7053         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7054                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7055                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7056                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7057         {
7058           ToggleSwitchgateSwitch(x, y + 1);
7059         }
7060         else if (smashed == EL_LIGHT_SWITCH ||
7061                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7062         {
7063           ToggleLightSwitch(x, y + 1);
7064         }
7065         else
7066         {
7067 #if 0
7068           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7069 #endif
7070
7071           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7072
7073           CheckElementChangeBySide(x, y + 1, smashed, element,
7074                                    CE_SWITCHED, CH_SIDE_TOP);
7075           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7076                                             CH_SIDE_TOP);
7077         }
7078       }
7079       else
7080       {
7081         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7082       }
7083     }
7084   }
7085
7086   /* play sound of magic wall / mill */
7087   if (!last_line &&
7088       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7089        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7090        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7091   {
7092     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7093       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7094     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7095       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7096     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7097       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7098
7099     return;
7100   }
7101
7102   /* play sound of object that hits the ground */
7103   if (last_line || object_hit)
7104     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7105 }
7106
7107 inline static void TurnRoundExt(int x, int y)
7108 {
7109   static struct
7110   {
7111     int dx, dy;
7112   } move_xy[] =
7113   {
7114     {  0,  0 },
7115     { -1,  0 },
7116     { +1,  0 },
7117     {  0,  0 },
7118     {  0, -1 },
7119     {  0,  0 }, { 0, 0 }, { 0, 0 },
7120     {  0, +1 }
7121   };
7122   static struct
7123   {
7124     int left, right, back;
7125   } turn[] =
7126   {
7127     { 0,        0,              0        },
7128     { MV_DOWN,  MV_UP,          MV_RIGHT },
7129     { MV_UP,    MV_DOWN,        MV_LEFT  },
7130     { 0,        0,              0        },
7131     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7132     { 0,        0,              0        },
7133     { 0,        0,              0        },
7134     { 0,        0,              0        },
7135     { MV_RIGHT, MV_LEFT,        MV_UP    }
7136   };
7137
7138   int element = Feld[x][y];
7139   int move_pattern = element_info[element].move_pattern;
7140
7141   int old_move_dir = MovDir[x][y];
7142   int left_dir  = turn[old_move_dir].left;
7143   int right_dir = turn[old_move_dir].right;
7144   int back_dir  = turn[old_move_dir].back;
7145
7146   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7147   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7148   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7149   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7150
7151   int left_x  = x + left_dx,  left_y  = y + left_dy;
7152   int right_x = x + right_dx, right_y = y + right_dy;
7153   int move_x  = x + move_dx,  move_y  = y + move_dy;
7154
7155   int xx, yy;
7156
7157   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7158   {
7159     TestIfBadThingTouchesOtherBadThing(x, y);
7160
7161     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7162       MovDir[x][y] = right_dir;
7163     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7164       MovDir[x][y] = left_dir;
7165
7166     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7167       MovDelay[x][y] = 9;
7168     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7169       MovDelay[x][y] = 1;
7170   }
7171   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7172   {
7173     TestIfBadThingTouchesOtherBadThing(x, y);
7174
7175     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7176       MovDir[x][y] = left_dir;
7177     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7178       MovDir[x][y] = right_dir;
7179
7180     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7181       MovDelay[x][y] = 9;
7182     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7183       MovDelay[x][y] = 1;
7184   }
7185   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7186   {
7187     TestIfBadThingTouchesOtherBadThing(x, y);
7188
7189     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7190       MovDir[x][y] = left_dir;
7191     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7192       MovDir[x][y] = right_dir;
7193
7194     if (MovDir[x][y] != old_move_dir)
7195       MovDelay[x][y] = 9;
7196   }
7197   else if (element == EL_YAMYAM)
7198   {
7199     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7200     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7201
7202     if (can_turn_left && can_turn_right)
7203       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7204     else if (can_turn_left)
7205       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7206     else if (can_turn_right)
7207       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7208     else
7209       MovDir[x][y] = back_dir;
7210
7211     MovDelay[x][y] = 16 + 16 * RND(3);
7212   }
7213   else if (element == EL_DARK_YAMYAM)
7214   {
7215     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7216                                                          left_x, left_y);
7217     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7218                                                          right_x, right_y);
7219
7220     if (can_turn_left && can_turn_right)
7221       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7222     else if (can_turn_left)
7223       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7224     else if (can_turn_right)
7225       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7226     else
7227       MovDir[x][y] = back_dir;
7228
7229     MovDelay[x][y] = 16 + 16 * RND(3);
7230   }
7231   else if (element == EL_PACMAN)
7232   {
7233     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7234     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7235
7236     if (can_turn_left && can_turn_right)
7237       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7238     else if (can_turn_left)
7239       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7240     else if (can_turn_right)
7241       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7242     else
7243       MovDir[x][y] = back_dir;
7244
7245     MovDelay[x][y] = 6 + RND(40);
7246   }
7247   else if (element == EL_PIG)
7248   {
7249     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7250     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7251     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7252     boolean should_turn_left, should_turn_right, should_move_on;
7253     int rnd_value = 24;
7254     int rnd = RND(rnd_value);
7255
7256     should_turn_left = (can_turn_left &&
7257                         (!can_move_on ||
7258                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7259                                                    y + back_dy + left_dy)));
7260     should_turn_right = (can_turn_right &&
7261                          (!can_move_on ||
7262                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7263                                                     y + back_dy + right_dy)));
7264     should_move_on = (can_move_on &&
7265                       (!can_turn_left ||
7266                        !can_turn_right ||
7267                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7268                                                  y + move_dy + left_dy) ||
7269                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7270                                                  y + move_dy + right_dy)));
7271
7272     if (should_turn_left || should_turn_right || should_move_on)
7273     {
7274       if (should_turn_left && should_turn_right && should_move_on)
7275         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7276                         rnd < 2 * rnd_value / 3 ? right_dir :
7277                         old_move_dir);
7278       else if (should_turn_left && should_turn_right)
7279         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7280       else if (should_turn_left && should_move_on)
7281         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7282       else if (should_turn_right && should_move_on)
7283         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7284       else if (should_turn_left)
7285         MovDir[x][y] = left_dir;
7286       else if (should_turn_right)
7287         MovDir[x][y] = right_dir;
7288       else if (should_move_on)
7289         MovDir[x][y] = old_move_dir;
7290     }
7291     else if (can_move_on && rnd > rnd_value / 8)
7292       MovDir[x][y] = old_move_dir;
7293     else if (can_turn_left && can_turn_right)
7294       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7295     else if (can_turn_left && rnd > rnd_value / 8)
7296       MovDir[x][y] = left_dir;
7297     else if (can_turn_right && rnd > rnd_value/8)
7298       MovDir[x][y] = right_dir;
7299     else
7300       MovDir[x][y] = back_dir;
7301
7302     xx = x + move_xy[MovDir[x][y]].dx;
7303     yy = y + move_xy[MovDir[x][y]].dy;
7304
7305     if (!IN_LEV_FIELD(xx, yy) ||
7306         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7307       MovDir[x][y] = old_move_dir;
7308
7309     MovDelay[x][y] = 0;
7310   }
7311   else if (element == EL_DRAGON)
7312   {
7313     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7314     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7315     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7316     int rnd_value = 24;
7317     int rnd = RND(rnd_value);
7318
7319     if (can_move_on && rnd > rnd_value / 8)
7320       MovDir[x][y] = old_move_dir;
7321     else if (can_turn_left && can_turn_right)
7322       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7323     else if (can_turn_left && rnd > rnd_value / 8)
7324       MovDir[x][y] = left_dir;
7325     else if (can_turn_right && rnd > rnd_value / 8)
7326       MovDir[x][y] = right_dir;
7327     else
7328       MovDir[x][y] = back_dir;
7329
7330     xx = x + move_xy[MovDir[x][y]].dx;
7331     yy = y + move_xy[MovDir[x][y]].dy;
7332
7333     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7334       MovDir[x][y] = old_move_dir;
7335
7336     MovDelay[x][y] = 0;
7337   }
7338   else if (element == EL_MOLE)
7339   {
7340     boolean can_move_on =
7341       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7342                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7343                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7344     if (!can_move_on)
7345     {
7346       boolean can_turn_left =
7347         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7348                               IS_AMOEBOID(Feld[left_x][left_y])));
7349
7350       boolean can_turn_right =
7351         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7352                               IS_AMOEBOID(Feld[right_x][right_y])));
7353
7354       if (can_turn_left && can_turn_right)
7355         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7356       else if (can_turn_left)
7357         MovDir[x][y] = left_dir;
7358       else
7359         MovDir[x][y] = right_dir;
7360     }
7361
7362     if (MovDir[x][y] != old_move_dir)
7363       MovDelay[x][y] = 9;
7364   }
7365   else if (element == EL_BALLOON)
7366   {
7367     MovDir[x][y] = game.wind_direction;
7368     MovDelay[x][y] = 0;
7369   }
7370   else if (element == EL_SPRING)
7371   {
7372 #if USE_NEW_SPRING_BUMPER
7373     if (MovDir[x][y] & MV_HORIZONTAL)
7374     {
7375       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7376           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7377       {
7378         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7379         ResetGfxAnimation(move_x, move_y);
7380         TEST_DrawLevelField(move_x, move_y);
7381
7382         MovDir[x][y] = back_dir;
7383       }
7384       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7385                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7386         MovDir[x][y] = MV_NONE;
7387     }
7388 #else
7389     if (MovDir[x][y] & MV_HORIZONTAL &&
7390         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7391          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7392       MovDir[x][y] = MV_NONE;
7393 #endif
7394
7395     MovDelay[x][y] = 0;
7396   }
7397   else if (element == EL_ROBOT ||
7398            element == EL_SATELLITE ||
7399            element == EL_PENGUIN ||
7400            element == EL_EMC_ANDROID)
7401   {
7402     int attr_x = -1, attr_y = -1;
7403
7404     if (AllPlayersGone)
7405     {
7406       attr_x = ExitX;
7407       attr_y = ExitY;
7408     }
7409     else
7410     {
7411       int i;
7412
7413       for (i = 0; i < MAX_PLAYERS; i++)
7414       {
7415         struct PlayerInfo *player = &stored_player[i];
7416         int jx = player->jx, jy = player->jy;
7417
7418         if (!player->active)
7419           continue;
7420
7421         if (attr_x == -1 ||
7422             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7423         {
7424           attr_x = jx;
7425           attr_y = jy;
7426         }
7427       }
7428     }
7429
7430     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7431         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7432          game.engine_version < VERSION_IDENT(3,1,0,0)))
7433     {
7434       attr_x = ZX;
7435       attr_y = ZY;
7436     }
7437
7438     if (element == EL_PENGUIN)
7439     {
7440       int i;
7441       static int xy[4][2] =
7442       {
7443         { 0, -1 },
7444         { -1, 0 },
7445         { +1, 0 },
7446         { 0, +1 }
7447       };
7448
7449       for (i = 0; i < NUM_DIRECTIONS; i++)
7450       {
7451         int ex = x + xy[i][0];
7452         int ey = y + xy[i][1];
7453
7454         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7455                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7456                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7457                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7458         {
7459           attr_x = ex;
7460           attr_y = ey;
7461           break;
7462         }
7463       }
7464     }
7465
7466     MovDir[x][y] = MV_NONE;
7467     if (attr_x < x)
7468       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7469     else if (attr_x > x)
7470       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7471     if (attr_y < y)
7472       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7473     else if (attr_y > y)
7474       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7475
7476     if (element == EL_ROBOT)
7477     {
7478       int newx, newy;
7479
7480       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7481         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7482       Moving2Blocked(x, y, &newx, &newy);
7483
7484       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7485         MovDelay[x][y] = 8 + 8 * !RND(3);
7486       else
7487         MovDelay[x][y] = 16;
7488     }
7489     else if (element == EL_PENGUIN)
7490     {
7491       int newx, newy;
7492
7493       MovDelay[x][y] = 1;
7494
7495       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7496       {
7497         boolean first_horiz = RND(2);
7498         int new_move_dir = MovDir[x][y];
7499
7500         MovDir[x][y] =
7501           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502         Moving2Blocked(x, y, &newx, &newy);
7503
7504         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7505           return;
7506
7507         MovDir[x][y] =
7508           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509         Moving2Blocked(x, y, &newx, &newy);
7510
7511         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7512           return;
7513
7514         MovDir[x][y] = old_move_dir;
7515         return;
7516       }
7517     }
7518     else if (element == EL_SATELLITE)
7519     {
7520       int newx, newy;
7521
7522       MovDelay[x][y] = 1;
7523
7524       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7525       {
7526         boolean first_horiz = RND(2);
7527         int new_move_dir = MovDir[x][y];
7528
7529         MovDir[x][y] =
7530           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7531         Moving2Blocked(x, y, &newx, &newy);
7532
7533         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7534           return;
7535
7536         MovDir[x][y] =
7537           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7538         Moving2Blocked(x, y, &newx, &newy);
7539
7540         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7541           return;
7542
7543         MovDir[x][y] = old_move_dir;
7544         return;
7545       }
7546     }
7547     else if (element == EL_EMC_ANDROID)
7548     {
7549       static int check_pos[16] =
7550       {
7551         -1,             /*  0 => (invalid)          */
7552         7,              /*  1 => MV_LEFT            */
7553         3,              /*  2 => MV_RIGHT           */
7554         -1,             /*  3 => (invalid)          */
7555         1,              /*  4 =>            MV_UP   */
7556         0,              /*  5 => MV_LEFT  | MV_UP   */
7557         2,              /*  6 => MV_RIGHT | MV_UP   */
7558         -1,             /*  7 => (invalid)          */
7559         5,              /*  8 =>            MV_DOWN */
7560         6,              /*  9 => MV_LEFT  | MV_DOWN */
7561         4,              /* 10 => MV_RIGHT | MV_DOWN */
7562         -1,             /* 11 => (invalid)          */
7563         -1,             /* 12 => (invalid)          */
7564         -1,             /* 13 => (invalid)          */
7565         -1,             /* 14 => (invalid)          */
7566         -1,             /* 15 => (invalid)          */
7567       };
7568       static struct
7569       {
7570         int dx, dy;
7571         int dir;
7572       } check_xy[8] =
7573       {
7574         { -1, -1,       MV_LEFT  | MV_UP   },
7575         {  0, -1,                  MV_UP   },
7576         { +1, -1,       MV_RIGHT | MV_UP   },
7577         { +1,  0,       MV_RIGHT           },
7578         { +1, +1,       MV_RIGHT | MV_DOWN },
7579         {  0, +1,                  MV_DOWN },
7580         { -1, +1,       MV_LEFT  | MV_DOWN },
7581         { -1,  0,       MV_LEFT            },
7582       };
7583       int start_pos, check_order;
7584       boolean can_clone = FALSE;
7585       int i;
7586
7587       /* check if there is any free field around current position */
7588       for (i = 0; i < 8; i++)
7589       {
7590         int newx = x + check_xy[i].dx;
7591         int newy = y + check_xy[i].dy;
7592
7593         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7594         {
7595           can_clone = TRUE;
7596
7597           break;
7598         }
7599       }
7600
7601       if (can_clone)            /* randomly find an element to clone */
7602       {
7603         can_clone = FALSE;
7604
7605         start_pos = check_pos[RND(8)];
7606         check_order = (RND(2) ? -1 : +1);
7607
7608         for (i = 0; i < 8; i++)
7609         {
7610           int pos_raw = start_pos + i * check_order;
7611           int pos = (pos_raw + 8) % 8;
7612           int newx = x + check_xy[pos].dx;
7613           int newy = y + check_xy[pos].dy;
7614
7615           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7616           {
7617             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7618             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7619
7620             Store[x][y] = Feld[newx][newy];
7621
7622             can_clone = TRUE;
7623
7624             break;
7625           }
7626         }
7627       }
7628
7629       if (can_clone)            /* randomly find a direction to move */
7630       {
7631         can_clone = FALSE;
7632
7633         start_pos = check_pos[RND(8)];
7634         check_order = (RND(2) ? -1 : +1);
7635
7636         for (i = 0; i < 8; i++)
7637         {
7638           int pos_raw = start_pos + i * check_order;
7639           int pos = (pos_raw + 8) % 8;
7640           int newx = x + check_xy[pos].dx;
7641           int newy = y + check_xy[pos].dy;
7642           int new_move_dir = check_xy[pos].dir;
7643
7644           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7645           {
7646             MovDir[x][y] = new_move_dir;
7647             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7648
7649             can_clone = TRUE;
7650
7651             break;
7652           }
7653         }
7654       }
7655
7656       if (can_clone)            /* cloning and moving successful */
7657         return;
7658
7659       /* cannot clone -- try to move towards player */
7660
7661       start_pos = check_pos[MovDir[x][y] & 0x0f];
7662       check_order = (RND(2) ? -1 : +1);
7663
7664       for (i = 0; i < 3; i++)
7665       {
7666         /* first check start_pos, then previous/next or (next/previous) pos */
7667         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7668         int pos = (pos_raw + 8) % 8;
7669         int newx = x + check_xy[pos].dx;
7670         int newy = y + check_xy[pos].dy;
7671         int new_move_dir = check_xy[pos].dir;
7672
7673         if (IS_PLAYER(newx, newy))
7674           break;
7675
7676         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7677         {
7678           MovDir[x][y] = new_move_dir;
7679           MovDelay[x][y] = level.android_move_time * 8 + 1;
7680
7681           break;
7682         }
7683       }
7684     }
7685   }
7686   else if (move_pattern == MV_TURNING_LEFT ||
7687            move_pattern == MV_TURNING_RIGHT ||
7688            move_pattern == MV_TURNING_LEFT_RIGHT ||
7689            move_pattern == MV_TURNING_RIGHT_LEFT ||
7690            move_pattern == MV_TURNING_RANDOM ||
7691            move_pattern == MV_ALL_DIRECTIONS)
7692   {
7693     boolean can_turn_left =
7694       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7695     boolean can_turn_right =
7696       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7697
7698     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7699       return;
7700
7701     if (move_pattern == MV_TURNING_LEFT)
7702       MovDir[x][y] = left_dir;
7703     else if (move_pattern == MV_TURNING_RIGHT)
7704       MovDir[x][y] = right_dir;
7705     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7706       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7707     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7708       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7709     else if (move_pattern == MV_TURNING_RANDOM)
7710       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7711                       can_turn_right && !can_turn_left ? right_dir :
7712                       RND(2) ? left_dir : right_dir);
7713     else if (can_turn_left && can_turn_right)
7714       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7715     else if (can_turn_left)
7716       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7717     else if (can_turn_right)
7718       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7719     else
7720       MovDir[x][y] = back_dir;
7721
7722     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7723   }
7724   else if (move_pattern == MV_HORIZONTAL ||
7725            move_pattern == MV_VERTICAL)
7726   {
7727     if (move_pattern & old_move_dir)
7728       MovDir[x][y] = back_dir;
7729     else if (move_pattern == MV_HORIZONTAL)
7730       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7731     else if (move_pattern == MV_VERTICAL)
7732       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7733
7734     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7735   }
7736   else if (move_pattern & MV_ANY_DIRECTION)
7737   {
7738     MovDir[x][y] = move_pattern;
7739     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7740   }
7741   else if (move_pattern & MV_WIND_DIRECTION)
7742   {
7743     MovDir[x][y] = game.wind_direction;
7744     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7745   }
7746   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7747   {
7748     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7749       MovDir[x][y] = left_dir;
7750     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7751       MovDir[x][y] = right_dir;
7752
7753     if (MovDir[x][y] != old_move_dir)
7754       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7755   }
7756   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7757   {
7758     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7759       MovDir[x][y] = right_dir;
7760     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7761       MovDir[x][y] = left_dir;
7762
7763     if (MovDir[x][y] != old_move_dir)
7764       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7765   }
7766   else if (move_pattern == MV_TOWARDS_PLAYER ||
7767            move_pattern == MV_AWAY_FROM_PLAYER)
7768   {
7769     int attr_x = -1, attr_y = -1;
7770     int newx, newy;
7771     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7772
7773     if (AllPlayersGone)
7774     {
7775       attr_x = ExitX;
7776       attr_y = ExitY;
7777     }
7778     else
7779     {
7780       int i;
7781
7782       for (i = 0; i < MAX_PLAYERS; i++)
7783       {
7784         struct PlayerInfo *player = &stored_player[i];
7785         int jx = player->jx, jy = player->jy;
7786
7787         if (!player->active)
7788           continue;
7789
7790         if (attr_x == -1 ||
7791             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7792         {
7793           attr_x = jx;
7794           attr_y = jy;
7795         }
7796       }
7797     }
7798
7799     MovDir[x][y] = MV_NONE;
7800     if (attr_x < x)
7801       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7802     else if (attr_x > x)
7803       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7804     if (attr_y < y)
7805       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7806     else if (attr_y > y)
7807       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7808
7809     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7810
7811     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7812     {
7813       boolean first_horiz = RND(2);
7814       int new_move_dir = MovDir[x][y];
7815
7816       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7817       {
7818         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7819         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7820
7821         return;
7822       }
7823
7824       MovDir[x][y] =
7825         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7826       Moving2Blocked(x, y, &newx, &newy);
7827
7828       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7829         return;
7830
7831       MovDir[x][y] =
7832         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7833       Moving2Blocked(x, y, &newx, &newy);
7834
7835       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7836         return;
7837
7838       MovDir[x][y] = old_move_dir;
7839     }
7840   }
7841   else if (move_pattern == MV_WHEN_PUSHED ||
7842            move_pattern == MV_WHEN_DROPPED)
7843   {
7844     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7845       MovDir[x][y] = MV_NONE;
7846
7847     MovDelay[x][y] = 0;
7848   }
7849   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7850   {
7851     static int test_xy[7][2] =
7852     {
7853       { 0, -1 },
7854       { -1, 0 },
7855       { +1, 0 },
7856       { 0, +1 },
7857       { 0, -1 },
7858       { -1, 0 },
7859       { +1, 0 },
7860     };
7861     static int test_dir[7] =
7862     {
7863       MV_UP,
7864       MV_LEFT,
7865       MV_RIGHT,
7866       MV_DOWN,
7867       MV_UP,
7868       MV_LEFT,
7869       MV_RIGHT,
7870     };
7871     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7872     int move_preference = -1000000;     /* start with very low preference */
7873     int new_move_dir = MV_NONE;
7874     int start_test = RND(4);
7875     int i;
7876
7877     for (i = 0; i < NUM_DIRECTIONS; i++)
7878     {
7879       int move_dir = test_dir[start_test + i];
7880       int move_dir_preference;
7881
7882       xx = x + test_xy[start_test + i][0];
7883       yy = y + test_xy[start_test + i][1];
7884
7885       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7886           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7887       {
7888         new_move_dir = move_dir;
7889
7890         break;
7891       }
7892
7893       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7894         continue;
7895
7896       move_dir_preference = -1 * RunnerVisit[xx][yy];
7897       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7898         move_dir_preference = PlayerVisit[xx][yy];
7899
7900       if (move_dir_preference > move_preference)
7901       {
7902         /* prefer field that has not been visited for the longest time */
7903         move_preference = move_dir_preference;
7904         new_move_dir = move_dir;
7905       }
7906       else if (move_dir_preference == move_preference &&
7907                move_dir == old_move_dir)
7908       {
7909         /* prefer last direction when all directions are preferred equally */
7910         move_preference = move_dir_preference;
7911         new_move_dir = move_dir;
7912       }
7913     }
7914
7915     MovDir[x][y] = new_move_dir;
7916     if (old_move_dir != new_move_dir)
7917       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7918   }
7919 }
7920
7921 static void TurnRound(int x, int y)
7922 {
7923   int direction = MovDir[x][y];
7924
7925   TurnRoundExt(x, y);
7926
7927   GfxDir[x][y] = MovDir[x][y];
7928
7929   if (direction != MovDir[x][y])
7930     GfxFrame[x][y] = 0;
7931
7932   if (MovDelay[x][y])
7933     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7934
7935   ResetGfxFrame(x, y, FALSE);
7936 }
7937
7938 static boolean JustBeingPushed(int x, int y)
7939 {
7940   int i;
7941
7942   for (i = 0; i < MAX_PLAYERS; i++)
7943   {
7944     struct PlayerInfo *player = &stored_player[i];
7945
7946     if (player->active && player->is_pushing && player->MovPos)
7947     {
7948       int next_jx = player->jx + (player->jx - player->last_jx);
7949       int next_jy = player->jy + (player->jy - player->last_jy);
7950
7951       if (x == next_jx && y == next_jy)
7952         return TRUE;
7953     }
7954   }
7955
7956   return FALSE;
7957 }
7958
7959 void StartMoving(int x, int y)
7960 {
7961   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7962   int element = Feld[x][y];
7963
7964   if (Stop[x][y])
7965     return;
7966
7967   if (MovDelay[x][y] == 0)
7968     GfxAction[x][y] = ACTION_DEFAULT;
7969
7970   if (CAN_FALL(element) && y < lev_fieldy - 1)
7971   {
7972     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7973         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7974       if (JustBeingPushed(x, y))
7975         return;
7976
7977     if (element == EL_QUICKSAND_FULL)
7978     {
7979       if (IS_FREE(x, y + 1))
7980       {
7981         InitMovingField(x, y, MV_DOWN);
7982         started_moving = TRUE;
7983
7984         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7985 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7986         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7987           Store[x][y] = EL_ROCK;
7988 #else
7989         Store[x][y] = EL_ROCK;
7990 #endif
7991
7992         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7993       }
7994       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7995       {
7996         if (!MovDelay[x][y])
7997         {
7998           MovDelay[x][y] = TILEY + 1;
7999
8000           ResetGfxAnimation(x, y);
8001           ResetGfxAnimation(x, y + 1);
8002         }
8003
8004         if (MovDelay[x][y])
8005         {
8006           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8007           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8008
8009           MovDelay[x][y]--;
8010           if (MovDelay[x][y])
8011             return;
8012         }
8013
8014         Feld[x][y] = EL_QUICKSAND_EMPTY;
8015         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8016         Store[x][y + 1] = Store[x][y];
8017         Store[x][y] = 0;
8018
8019         PlayLevelSoundAction(x, y, ACTION_FILLING);
8020       }
8021       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8022       {
8023         if (!MovDelay[x][y])
8024         {
8025           MovDelay[x][y] = TILEY + 1;
8026
8027           ResetGfxAnimation(x, y);
8028           ResetGfxAnimation(x, y + 1);
8029         }
8030
8031         if (MovDelay[x][y])
8032         {
8033           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8034           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8035
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Feld[x][y] = EL_QUICKSAND_EMPTY;
8042         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8043         Store[x][y + 1] = Store[x][y];
8044         Store[x][y] = 0;
8045
8046         PlayLevelSoundAction(x, y, ACTION_FILLING);
8047       }
8048     }
8049     else if (element == EL_QUICKSAND_FAST_FULL)
8050     {
8051       if (IS_FREE(x, y + 1))
8052       {
8053         InitMovingField(x, y, MV_DOWN);
8054         started_moving = TRUE;
8055
8056         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8057 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8058         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8059           Store[x][y] = EL_ROCK;
8060 #else
8061         Store[x][y] = EL_ROCK;
8062 #endif
8063
8064         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8065       }
8066       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         if (!MovDelay[x][y])
8069         {
8070           MovDelay[x][y] = TILEY + 1;
8071
8072           ResetGfxAnimation(x, y);
8073           ResetGfxAnimation(x, y + 1);
8074         }
8075
8076         if (MovDelay[x][y])
8077         {
8078           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8079           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8080
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8087         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088         Store[x][y + 1] = Store[x][y];
8089         Store[x][y] = 0;
8090
8091         PlayLevelSoundAction(x, y, ACTION_FILLING);
8092       }
8093       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8094       {
8095         if (!MovDelay[x][y])
8096         {
8097           MovDelay[x][y] = TILEY + 1;
8098
8099           ResetGfxAnimation(x, y);
8100           ResetGfxAnimation(x, y + 1);
8101         }
8102
8103         if (MovDelay[x][y])
8104         {
8105           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8106           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8107
8108           MovDelay[x][y]--;
8109           if (MovDelay[x][y])
8110             return;
8111         }
8112
8113         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8114         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8115         Store[x][y + 1] = Store[x][y];
8116         Store[x][y] = 0;
8117
8118         PlayLevelSoundAction(x, y, ACTION_FILLING);
8119       }
8120     }
8121     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8122              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8123     {
8124       InitMovingField(x, y, MV_DOWN);
8125       started_moving = TRUE;
8126
8127       Feld[x][y] = EL_QUICKSAND_FILLING;
8128       Store[x][y] = element;
8129
8130       PlayLevelSoundAction(x, y, ACTION_FILLING);
8131     }
8132     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8133              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8134     {
8135       InitMovingField(x, y, MV_DOWN);
8136       started_moving = TRUE;
8137
8138       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8139       Store[x][y] = element;
8140
8141       PlayLevelSoundAction(x, y, ACTION_FILLING);
8142     }
8143     else if (element == EL_MAGIC_WALL_FULL)
8144     {
8145       if (IS_FREE(x, y + 1))
8146       {
8147         InitMovingField(x, y, MV_DOWN);
8148         started_moving = TRUE;
8149
8150         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8151         Store[x][y] = EL_CHANGED(Store[x][y]);
8152       }
8153       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8154       {
8155         if (!MovDelay[x][y])
8156           MovDelay[x][y] = TILEY / 4 + 1;
8157
8158         if (MovDelay[x][y])
8159         {
8160           MovDelay[x][y]--;
8161           if (MovDelay[x][y])
8162             return;
8163         }
8164
8165         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8166         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8167         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8168         Store[x][y] = 0;
8169       }
8170     }
8171     else if (element == EL_BD_MAGIC_WALL_FULL)
8172     {
8173       if (IS_FREE(x, y + 1))
8174       {
8175         InitMovingField(x, y, MV_DOWN);
8176         started_moving = TRUE;
8177
8178         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8179         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8180       }
8181       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8182       {
8183         if (!MovDelay[x][y])
8184           MovDelay[x][y] = TILEY / 4 + 1;
8185
8186         if (MovDelay[x][y])
8187         {
8188           MovDelay[x][y]--;
8189           if (MovDelay[x][y])
8190             return;
8191         }
8192
8193         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8194         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8195         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8196         Store[x][y] = 0;
8197       }
8198     }
8199     else if (element == EL_DC_MAGIC_WALL_FULL)
8200     {
8201       if (IS_FREE(x, y + 1))
8202       {
8203         InitMovingField(x, y, MV_DOWN);
8204         started_moving = TRUE;
8205
8206         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8207         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8208       }
8209       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8210       {
8211         if (!MovDelay[x][y])
8212           MovDelay[x][y] = TILEY / 4 + 1;
8213
8214         if (MovDelay[x][y])
8215         {
8216           MovDelay[x][y]--;
8217           if (MovDelay[x][y])
8218             return;
8219         }
8220
8221         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8222         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8223         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8224         Store[x][y] = 0;
8225       }
8226     }
8227     else if ((CAN_PASS_MAGIC_WALL(element) &&
8228               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8229                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8230              (CAN_PASS_DC_MAGIC_WALL(element) &&
8231               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8232
8233     {
8234       InitMovingField(x, y, MV_DOWN);
8235       started_moving = TRUE;
8236
8237       Feld[x][y] =
8238         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8239          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8240          EL_DC_MAGIC_WALL_FILLING);
8241       Store[x][y] = element;
8242     }
8243     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8244     {
8245       SplashAcid(x, y + 1);
8246
8247       InitMovingField(x, y, MV_DOWN);
8248       started_moving = TRUE;
8249
8250       Store[x][y] = EL_ACID;
8251     }
8252     else if (
8253 #if USE_FIX_IMPACT_COLLISION
8254              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8255               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8256 #else
8257              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8258               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8259 #endif
8260              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8261               CAN_FALL(element) && WasJustFalling[x][y] &&
8262               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8263
8264              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8265               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8266               (Feld[x][y + 1] == EL_BLOCKED)))
8267     {
8268       /* this is needed for a special case not covered by calling "Impact()"
8269          from "ContinueMoving()": if an element moves to a tile directly below
8270          another element which was just falling on that tile (which was empty
8271          in the previous frame), the falling element above would just stop
8272          instead of smashing the element below (in previous version, the above
8273          element was just checked for "moving" instead of "falling", resulting
8274          in incorrect smashes caused by horizontal movement of the above
8275          element; also, the case of the player being the element to smash was
8276          simply not covered here... :-/ ) */
8277
8278       CheckCollision[x][y] = 0;
8279       CheckImpact[x][y] = 0;
8280
8281       Impact(x, y);
8282     }
8283     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8284     {
8285       if (MovDir[x][y] == MV_NONE)
8286       {
8287         InitMovingField(x, y, MV_DOWN);
8288         started_moving = TRUE;
8289       }
8290     }
8291     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8292     {
8293       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8294         MovDir[x][y] = MV_DOWN;
8295
8296       InitMovingField(x, y, MV_DOWN);
8297       started_moving = TRUE;
8298     }
8299     else if (element == EL_AMOEBA_DROP)
8300     {
8301       Feld[x][y] = EL_AMOEBA_GROWING;
8302       Store[x][y] = EL_AMOEBA_WET;
8303     }
8304     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8305               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8306              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8307              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8308     {
8309       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8310                                 (IS_FREE(x - 1, y + 1) ||
8311                                  Feld[x - 1][y + 1] == EL_ACID));
8312       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8313                                 (IS_FREE(x + 1, y + 1) ||
8314                                  Feld[x + 1][y + 1] == EL_ACID));
8315       boolean can_fall_any  = (can_fall_left || can_fall_right);
8316       boolean can_fall_both = (can_fall_left && can_fall_right);
8317       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8318
8319 #if USE_NEW_ALL_SLIPPERY
8320       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8321       {
8322         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8323           can_fall_right = FALSE;
8324         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8325           can_fall_left = FALSE;
8326         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8327           can_fall_right = FALSE;
8328         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8329           can_fall_left = FALSE;
8330
8331         can_fall_any  = (can_fall_left || can_fall_right);
8332         can_fall_both = FALSE;
8333       }
8334 #else
8335       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8336       {
8337         if (slippery_type == SLIPPERY_ONLY_LEFT)
8338           can_fall_right = FALSE;
8339         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8340           can_fall_left = FALSE;
8341         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8342           can_fall_right = FALSE;
8343         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8344           can_fall_left = FALSE;
8345
8346         can_fall_any  = (can_fall_left || can_fall_right);
8347         can_fall_both = (can_fall_left && can_fall_right);
8348       }
8349 #endif
8350
8351 #if USE_NEW_ALL_SLIPPERY
8352 #else
8353 #if USE_NEW_SP_SLIPPERY
8354       /* !!! better use the same properties as for custom elements here !!! */
8355       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8356                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8357       {
8358         can_fall_right = FALSE;         /* slip down on left side */
8359         can_fall_both = FALSE;
8360       }
8361 #endif
8362 #endif
8363
8364 #if USE_NEW_ALL_SLIPPERY
8365       if (can_fall_both)
8366       {
8367         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8368           can_fall_right = FALSE;       /* slip down on left side */
8369         else
8370           can_fall_left = !(can_fall_right = RND(2));
8371
8372         can_fall_both = FALSE;
8373       }
8374 #else
8375       if (can_fall_both)
8376       {
8377         if (game.emulation == EMU_BOULDERDASH ||
8378             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8379           can_fall_right = FALSE;       /* slip down on left side */
8380         else
8381           can_fall_left = !(can_fall_right = RND(2));
8382
8383         can_fall_both = FALSE;
8384       }
8385 #endif
8386
8387       if (can_fall_any)
8388       {
8389         /* if not determined otherwise, prefer left side for slipping down */
8390         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8391         started_moving = TRUE;
8392       }
8393     }
8394 #if 0
8395     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8396 #else
8397     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8398 #endif
8399     {
8400       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8401       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8402       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8403       int belt_dir = game.belt_dir[belt_nr];
8404
8405       if ((belt_dir == MV_LEFT  && left_is_free) ||
8406           (belt_dir == MV_RIGHT && right_is_free))
8407       {
8408         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8409
8410         InitMovingField(x, y, belt_dir);
8411         started_moving = TRUE;
8412
8413         Pushed[x][y] = TRUE;
8414         Pushed[nextx][y] = TRUE;
8415
8416         GfxAction[x][y] = ACTION_DEFAULT;
8417       }
8418       else
8419       {
8420         MovDir[x][y] = 0;       /* if element was moving, stop it */
8421       }
8422     }
8423   }
8424
8425   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8426 #if 0
8427   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8428 #else
8429   if (CAN_MOVE(element) && !started_moving)
8430 #endif
8431   {
8432     int move_pattern = element_info[element].move_pattern;
8433     int newx, newy;
8434
8435 #if 0
8436 #if DEBUG
8437     if (MovDir[x][y] == MV_NONE)
8438     {
8439       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8440              x, y, element, element_info[element].token_name);
8441       printf("StartMoving(): This should never happen!\n");
8442     }
8443 #endif
8444 #endif
8445
8446     Moving2Blocked(x, y, &newx, &newy);
8447
8448     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8449       return;
8450
8451     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8452         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8453     {
8454       WasJustMoving[x][y] = 0;
8455       CheckCollision[x][y] = 0;
8456
8457       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8458
8459       if (Feld[x][y] != element)        /* element has changed */
8460         return;
8461     }
8462
8463     if (!MovDelay[x][y])        /* start new movement phase */
8464     {
8465       /* all objects that can change their move direction after each step
8466          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8467
8468       if (element != EL_YAMYAM &&
8469           element != EL_DARK_YAMYAM &&
8470           element != EL_PACMAN &&
8471           !(move_pattern & MV_ANY_DIRECTION) &&
8472           move_pattern != MV_TURNING_LEFT &&
8473           move_pattern != MV_TURNING_RIGHT &&
8474           move_pattern != MV_TURNING_LEFT_RIGHT &&
8475           move_pattern != MV_TURNING_RIGHT_LEFT &&
8476           move_pattern != MV_TURNING_RANDOM)
8477       {
8478         TurnRound(x, y);
8479
8480         if (MovDelay[x][y] && (element == EL_BUG ||
8481                                element == EL_SPACESHIP ||
8482                                element == EL_SP_SNIKSNAK ||
8483                                element == EL_SP_ELECTRON ||
8484                                element == EL_MOLE))
8485           TEST_DrawLevelField(x, y);
8486       }
8487     }
8488
8489     if (MovDelay[x][y])         /* wait some time before next movement */
8490     {
8491       MovDelay[x][y]--;
8492
8493       if (element == EL_ROBOT ||
8494           element == EL_YAMYAM ||
8495           element == EL_DARK_YAMYAM)
8496       {
8497         DrawLevelElementAnimationIfNeeded(x, y, element);
8498         PlayLevelSoundAction(x, y, ACTION_WAITING);
8499       }
8500       else if (element == EL_SP_ELECTRON)
8501         DrawLevelElementAnimationIfNeeded(x, y, element);
8502       else if (element == EL_DRAGON)
8503       {
8504         int i;
8505         int dir = MovDir[x][y];
8506         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8507         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8508         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8509                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8510                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8511                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8512         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8513
8514         GfxAction[x][y] = ACTION_ATTACKING;
8515
8516         if (IS_PLAYER(x, y))
8517           DrawPlayerField(x, y);
8518         else
8519           TEST_DrawLevelField(x, y);
8520
8521         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8522
8523         for (i = 1; i <= 3; i++)
8524         {
8525           int xx = x + i * dx;
8526           int yy = y + i * dy;
8527           int sx = SCREENX(xx);
8528           int sy = SCREENY(yy);
8529           int flame_graphic = graphic + (i - 1);
8530
8531           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8532             break;
8533
8534           if (MovDelay[x][y])
8535           {
8536             int flamed = MovingOrBlocked2Element(xx, yy);
8537
8538             /* !!! */
8539 #if 0
8540             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8541               Bang(xx, yy);
8542             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8543               RemoveMovingField(xx, yy);
8544             else
8545               RemoveField(xx, yy);
8546 #else
8547             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8548               Bang(xx, yy);
8549             else
8550               RemoveMovingField(xx, yy);
8551 #endif
8552
8553             ChangeDelay[xx][yy] = 0;
8554
8555             Feld[xx][yy] = EL_FLAMES;
8556
8557             if (IN_SCR_FIELD(sx, sy))
8558             {
8559               TEST_DrawLevelFieldCrumbled(xx, yy);
8560               DrawGraphic(sx, sy, flame_graphic, frame);
8561             }
8562           }
8563           else
8564           {
8565             if (Feld[xx][yy] == EL_FLAMES)
8566               Feld[xx][yy] = EL_EMPTY;
8567             TEST_DrawLevelField(xx, yy);
8568           }
8569         }
8570       }
8571
8572       if (MovDelay[x][y])       /* element still has to wait some time */
8573       {
8574         PlayLevelSoundAction(x, y, ACTION_WAITING);
8575
8576         return;
8577       }
8578     }
8579
8580     /* now make next step */
8581
8582     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8583
8584     if (DONT_COLLIDE_WITH(element) &&
8585         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8586         !PLAYER_ENEMY_PROTECTED(newx, newy))
8587     {
8588       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8589
8590       return;
8591     }
8592
8593     else if (CAN_MOVE_INTO_ACID(element) &&
8594              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8595              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8596              (MovDir[x][y] == MV_DOWN ||
8597               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8598     {
8599       SplashAcid(newx, newy);
8600       Store[x][y] = EL_ACID;
8601     }
8602     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8603     {
8604       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8605           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8606           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8607           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8608       {
8609         RemoveField(x, y);
8610         TEST_DrawLevelField(x, y);
8611
8612         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8613         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8614           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8615
8616         local_player->friends_still_needed--;
8617         if (!local_player->friends_still_needed &&
8618             !local_player->GameOver && AllPlayersGone)
8619           PlayerWins(local_player);
8620
8621         return;
8622       }
8623       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8624       {
8625         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8626           TEST_DrawLevelField(newx, newy);
8627         else
8628           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8629       }
8630       else if (!IS_FREE(newx, newy))
8631       {
8632         GfxAction[x][y] = ACTION_WAITING;
8633
8634         if (IS_PLAYER(x, y))
8635           DrawPlayerField(x, y);
8636         else
8637           TEST_DrawLevelField(x, y);
8638
8639         return;
8640       }
8641     }
8642     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8643     {
8644       if (IS_FOOD_PIG(Feld[newx][newy]))
8645       {
8646         if (IS_MOVING(newx, newy))
8647           RemoveMovingField(newx, newy);
8648         else
8649         {
8650           Feld[newx][newy] = EL_EMPTY;
8651           TEST_DrawLevelField(newx, newy);
8652         }
8653
8654         PlayLevelSound(x, y, SND_PIG_DIGGING);
8655       }
8656       else if (!IS_FREE(newx, newy))
8657       {
8658         if (IS_PLAYER(x, y))
8659           DrawPlayerField(x, y);
8660         else
8661           TEST_DrawLevelField(x, y);
8662
8663         return;
8664       }
8665     }
8666     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8667     {
8668       if (Store[x][y] != EL_EMPTY)
8669       {
8670         boolean can_clone = FALSE;
8671         int xx, yy;
8672
8673         /* check if element to clone is still there */
8674         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8675         {
8676           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8677           {
8678             can_clone = TRUE;
8679
8680             break;
8681           }
8682         }
8683
8684         /* cannot clone or target field not free anymore -- do not clone */
8685         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8686           Store[x][y] = EL_EMPTY;
8687       }
8688
8689       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8690       {
8691         if (IS_MV_DIAGONAL(MovDir[x][y]))
8692         {
8693           int diagonal_move_dir = MovDir[x][y];
8694           int stored = Store[x][y];
8695           int change_delay = 8;
8696           int graphic;
8697
8698           /* android is moving diagonally */
8699
8700           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8701
8702           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8703           GfxElement[x][y] = EL_EMC_ANDROID;
8704           GfxAction[x][y] = ACTION_SHRINKING;
8705           GfxDir[x][y] = diagonal_move_dir;
8706           ChangeDelay[x][y] = change_delay;
8707
8708           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8709                                    GfxDir[x][y]);
8710
8711           DrawLevelGraphicAnimation(x, y, graphic);
8712           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8713
8714           if (Feld[newx][newy] == EL_ACID)
8715           {
8716             SplashAcid(newx, newy);
8717
8718             return;
8719           }
8720
8721           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8722
8723           Store[newx][newy] = EL_EMC_ANDROID;
8724           GfxElement[newx][newy] = EL_EMC_ANDROID;
8725           GfxAction[newx][newy] = ACTION_GROWING;
8726           GfxDir[newx][newy] = diagonal_move_dir;
8727           ChangeDelay[newx][newy] = change_delay;
8728
8729           graphic = el_act_dir2img(GfxElement[newx][newy],
8730                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8731
8732           DrawLevelGraphicAnimation(newx, newy, graphic);
8733           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8734
8735           return;
8736         }
8737         else
8738         {
8739           Feld[newx][newy] = EL_EMPTY;
8740           TEST_DrawLevelField(newx, newy);
8741
8742           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8743         }
8744       }
8745       else if (!IS_FREE(newx, newy))
8746       {
8747 #if 0
8748         if (IS_PLAYER(x, y))
8749           DrawPlayerField(x, y);
8750         else
8751           TEST_DrawLevelField(x, y);
8752 #endif
8753
8754         return;
8755       }
8756     }
8757     else if (IS_CUSTOM_ELEMENT(element) &&
8758              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8759     {
8760 #if 1
8761       if (!DigFieldByCE(newx, newy, element))
8762         return;
8763 #else
8764       int new_element = Feld[newx][newy];
8765
8766       if (!IS_FREE(newx, newy))
8767       {
8768         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8769                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8770                       ACTION_BREAKING);
8771
8772         /* no element can dig solid indestructible elements */
8773         if (IS_INDESTRUCTIBLE(new_element) &&
8774             !IS_DIGGABLE(new_element) &&
8775             !IS_COLLECTIBLE(new_element))
8776           return;
8777
8778         if (AmoebaNr[newx][newy] &&
8779             (new_element == EL_AMOEBA_FULL ||
8780              new_element == EL_BD_AMOEBA ||
8781              new_element == EL_AMOEBA_GROWING))
8782         {
8783           AmoebaCnt[AmoebaNr[newx][newy]]--;
8784           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8785         }
8786
8787         if (IS_MOVING(newx, newy))
8788           RemoveMovingField(newx, newy);
8789         else
8790         {
8791           RemoveField(newx, newy);
8792           TEST_DrawLevelField(newx, newy);
8793         }
8794
8795         /* if digged element was about to explode, prevent the explosion */
8796         ExplodeField[newx][newy] = EX_TYPE_NONE;
8797
8798         PlayLevelSoundAction(x, y, action);
8799       }
8800
8801       Store[newx][newy] = EL_EMPTY;
8802
8803 #if 1
8804       /* this makes it possible to leave the removed element again */
8805       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8806         Store[newx][newy] = new_element;
8807 #else
8808       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8809       {
8810         int move_leave_element = element_info[element].move_leave_element;
8811
8812         /* this makes it possible to leave the removed element again */
8813         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8814                              new_element : move_leave_element);
8815       }
8816 #endif
8817
8818 #endif
8819
8820       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8821       {
8822         RunnerVisit[x][y] = FrameCounter;
8823         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8824       }
8825     }
8826     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8827     {
8828       if (!IS_FREE(newx, newy))
8829       {
8830         if (IS_PLAYER(x, y))
8831           DrawPlayerField(x, y);
8832         else
8833           TEST_DrawLevelField(x, y);
8834
8835         return;
8836       }
8837       else
8838       {
8839         boolean wanna_flame = !RND(10);
8840         int dx = newx - x, dy = newy - y;
8841         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8842         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8843         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8844                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8845         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8846                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8847
8848         if ((wanna_flame ||
8849              IS_CLASSIC_ENEMY(element1) ||
8850              IS_CLASSIC_ENEMY(element2)) &&
8851             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8852             element1 != EL_FLAMES && element2 != EL_FLAMES)
8853         {
8854           ResetGfxAnimation(x, y);
8855           GfxAction[x][y] = ACTION_ATTACKING;
8856
8857           if (IS_PLAYER(x, y))
8858             DrawPlayerField(x, y);
8859           else
8860             TEST_DrawLevelField(x, y);
8861
8862           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8863
8864           MovDelay[x][y] = 50;
8865
8866           /* !!! */
8867 #if 0
8868           RemoveField(newx, newy);
8869 #endif
8870           Feld[newx][newy] = EL_FLAMES;
8871           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8872           {
8873 #if 0
8874             RemoveField(newx1, newy1);
8875 #endif
8876             Feld[newx1][newy1] = EL_FLAMES;
8877           }
8878           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8879           {
8880 #if 0
8881             RemoveField(newx2, newy2);
8882 #endif
8883             Feld[newx2][newy2] = EL_FLAMES;
8884           }
8885
8886           return;
8887         }
8888       }
8889     }
8890     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8891              Feld[newx][newy] == EL_DIAMOND)
8892     {
8893       if (IS_MOVING(newx, newy))
8894         RemoveMovingField(newx, newy);
8895       else
8896       {
8897         Feld[newx][newy] = EL_EMPTY;
8898         TEST_DrawLevelField(newx, newy);
8899       }
8900
8901       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8902     }
8903     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8904              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8905     {
8906       if (AmoebaNr[newx][newy])
8907       {
8908         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8909         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8910             Feld[newx][newy] == EL_BD_AMOEBA)
8911           AmoebaCnt[AmoebaNr[newx][newy]]--;
8912       }
8913
8914 #if 0
8915       /* !!! test !!! */
8916       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8917       {
8918         RemoveMovingField(newx, newy);
8919       }
8920 #else
8921       if (IS_MOVING(newx, newy))
8922       {
8923         RemoveMovingField(newx, newy);
8924       }
8925 #endif
8926       else
8927       {
8928         Feld[newx][newy] = EL_EMPTY;
8929         TEST_DrawLevelField(newx, newy);
8930       }
8931
8932       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8933     }
8934     else if ((element == EL_PACMAN || element == EL_MOLE)
8935              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8936     {
8937       if (AmoebaNr[newx][newy])
8938       {
8939         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8940         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8941             Feld[newx][newy] == EL_BD_AMOEBA)
8942           AmoebaCnt[AmoebaNr[newx][newy]]--;
8943       }
8944
8945       if (element == EL_MOLE)
8946       {
8947         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8948         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8949
8950         ResetGfxAnimation(x, y);
8951         GfxAction[x][y] = ACTION_DIGGING;
8952         TEST_DrawLevelField(x, y);
8953
8954         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8955
8956         return;                         /* wait for shrinking amoeba */
8957       }
8958       else      /* element == EL_PACMAN */
8959       {
8960         Feld[newx][newy] = EL_EMPTY;
8961         TEST_DrawLevelField(newx, newy);
8962         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8963       }
8964     }
8965     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8966              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8967               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8968     {
8969       /* wait for shrinking amoeba to completely disappear */
8970       return;
8971     }
8972     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8973     {
8974       /* object was running against a wall */
8975
8976       TurnRound(x, y);
8977
8978 #if 0
8979       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8980       if (move_pattern & MV_ANY_DIRECTION &&
8981           move_pattern == MovDir[x][y])
8982       {
8983         int blocking_element =
8984           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8985
8986         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8987                                  MovDir[x][y]);
8988
8989         element = Feld[x][y];   /* element might have changed */
8990       }
8991 #endif
8992
8993       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8994         DrawLevelElementAnimation(x, y, element);
8995
8996       if (DONT_TOUCH(element))
8997         TestIfBadThingTouchesPlayer(x, y);
8998
8999       return;
9000     }
9001
9002     InitMovingField(x, y, MovDir[x][y]);
9003
9004     PlayLevelSoundAction(x, y, ACTION_MOVING);
9005   }
9006
9007   if (MovDir[x][y])
9008     ContinueMoving(x, y);
9009 }
9010
9011 void ContinueMoving(int x, int y)
9012 {
9013   int element = Feld[x][y];
9014   struct ElementInfo *ei = &element_info[element];
9015   int direction = MovDir[x][y];
9016   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9017   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9018   int newx = x + dx, newy = y + dy;
9019   int stored = Store[x][y];
9020   int stored_new = Store[newx][newy];
9021   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9022   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9023   boolean last_line = (newy == lev_fieldy - 1);
9024
9025   MovPos[x][y] += getElementMoveStepsize(x, y);
9026
9027   if (pushed_by_player) /* special case: moving object pushed by player */
9028     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9029
9030   if (ABS(MovPos[x][y]) < TILEX)
9031   {
9032 #if 0
9033     int ee = Feld[x][y];
9034     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9035     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9036
9037     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9038            x, y, ABS(MovPos[x][y]),
9039            ee, gg, ff,
9040            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9041 #endif
9042
9043     TEST_DrawLevelField(x, y);
9044
9045     return;     /* element is still moving */
9046   }
9047
9048   /* element reached destination field */
9049
9050   Feld[x][y] = EL_EMPTY;
9051   Feld[newx][newy] = element;
9052   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9053
9054   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9055   {
9056     element = Feld[newx][newy] = EL_ACID;
9057   }
9058   else if (element == EL_MOLE)
9059   {
9060     Feld[x][y] = EL_SAND;
9061
9062     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9063   }
9064   else if (element == EL_QUICKSAND_FILLING)
9065   {
9066     element = Feld[newx][newy] = get_next_element(element);
9067     Store[newx][newy] = Store[x][y];
9068   }
9069   else if (element == EL_QUICKSAND_EMPTYING)
9070   {
9071     Feld[x][y] = get_next_element(element);
9072     element = Feld[newx][newy] = Store[x][y];
9073   }
9074   else if (element == EL_QUICKSAND_FAST_FILLING)
9075   {
9076     element = Feld[newx][newy] = get_next_element(element);
9077     Store[newx][newy] = Store[x][y];
9078   }
9079   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9080   {
9081     Feld[x][y] = get_next_element(element);
9082     element = Feld[newx][newy] = Store[x][y];
9083   }
9084   else if (element == EL_MAGIC_WALL_FILLING)
9085   {
9086     element = Feld[newx][newy] = get_next_element(element);
9087     if (!game.magic_wall_active)
9088       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9089     Store[newx][newy] = Store[x][y];
9090   }
9091   else if (element == EL_MAGIC_WALL_EMPTYING)
9092   {
9093     Feld[x][y] = get_next_element(element);
9094     if (!game.magic_wall_active)
9095       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9096     element = Feld[newx][newy] = Store[x][y];
9097
9098 #if USE_NEW_CUSTOM_VALUE
9099     InitField(newx, newy, FALSE);
9100 #endif
9101   }
9102   else if (element == EL_BD_MAGIC_WALL_FILLING)
9103   {
9104     element = Feld[newx][newy] = get_next_element(element);
9105     if (!game.magic_wall_active)
9106       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9107     Store[newx][newy] = Store[x][y];
9108   }
9109   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9110   {
9111     Feld[x][y] = get_next_element(element);
9112     if (!game.magic_wall_active)
9113       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9114     element = Feld[newx][newy] = Store[x][y];
9115
9116 #if USE_NEW_CUSTOM_VALUE
9117     InitField(newx, newy, FALSE);
9118 #endif
9119   }
9120   else if (element == EL_DC_MAGIC_WALL_FILLING)
9121   {
9122     element = Feld[newx][newy] = get_next_element(element);
9123     if (!game.magic_wall_active)
9124       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9125     Store[newx][newy] = Store[x][y];
9126   }
9127   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9128   {
9129     Feld[x][y] = get_next_element(element);
9130     if (!game.magic_wall_active)
9131       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9132     element = Feld[newx][newy] = Store[x][y];
9133
9134 #if USE_NEW_CUSTOM_VALUE
9135     InitField(newx, newy, FALSE);
9136 #endif
9137   }
9138   else if (element == EL_AMOEBA_DROPPING)
9139   {
9140     Feld[x][y] = get_next_element(element);
9141     element = Feld[newx][newy] = Store[x][y];
9142   }
9143   else if (element == EL_SOKOBAN_OBJECT)
9144   {
9145     if (Back[x][y])
9146       Feld[x][y] = Back[x][y];
9147
9148     if (Back[newx][newy])
9149       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9150
9151     Back[x][y] = Back[newx][newy] = 0;
9152   }
9153
9154   Store[x][y] = EL_EMPTY;
9155   MovPos[x][y] = 0;
9156   MovDir[x][y] = 0;
9157   MovDelay[x][y] = 0;
9158
9159   MovDelay[newx][newy] = 0;
9160
9161   if (CAN_CHANGE_OR_HAS_ACTION(element))
9162   {
9163     /* copy element change control values to new field */
9164     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9165     ChangePage[newx][newy]  = ChangePage[x][y];
9166     ChangeCount[newx][newy] = ChangeCount[x][y];
9167     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9168   }
9169
9170 #if USE_NEW_CUSTOM_VALUE
9171   CustomValue[newx][newy] = CustomValue[x][y];
9172 #endif
9173
9174   ChangeDelay[x][y] = 0;
9175   ChangePage[x][y] = -1;
9176   ChangeCount[x][y] = 0;
9177   ChangeEvent[x][y] = -1;
9178
9179 #if USE_NEW_CUSTOM_VALUE
9180   CustomValue[x][y] = 0;
9181 #endif
9182
9183   /* copy animation control values to new field */
9184   GfxFrame[newx][newy]  = GfxFrame[x][y];
9185   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9186   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9187   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9188
9189   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9190
9191   /* some elements can leave other elements behind after moving */
9192 #if 1
9193   if (ei->move_leave_element != EL_EMPTY &&
9194       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9195       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9196 #else
9197   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9198       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9199       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9200 #endif
9201   {
9202     int move_leave_element = ei->move_leave_element;
9203
9204 #if 1
9205 #if 1
9206     /* this makes it possible to leave the removed element again */
9207     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9208       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9209 #else
9210     /* this makes it possible to leave the removed element again */
9211     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9212       move_leave_element = stored;
9213 #endif
9214 #else
9215     /* this makes it possible to leave the removed element again */
9216     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9217         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9218       move_leave_element = stored;
9219 #endif
9220
9221     Feld[x][y] = move_leave_element;
9222
9223     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9224       MovDir[x][y] = direction;
9225
9226     InitField(x, y, FALSE);
9227
9228     if (GFX_CRUMBLED(Feld[x][y]))
9229       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9230
9231     if (ELEM_IS_PLAYER(move_leave_element))
9232       RelocatePlayer(x, y, move_leave_element);
9233   }
9234
9235   /* do this after checking for left-behind element */
9236   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9237
9238   if (!CAN_MOVE(element) ||
9239       (CAN_FALL(element) && direction == MV_DOWN &&
9240        (element == EL_SPRING ||
9241         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9242         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9243     GfxDir[x][y] = MovDir[newx][newy] = 0;
9244
9245   TEST_DrawLevelField(x, y);
9246   TEST_DrawLevelField(newx, newy);
9247
9248   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9249
9250   /* prevent pushed element from moving on in pushed direction */
9251   if (pushed_by_player && CAN_MOVE(element) &&
9252       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9253       !(element_info[element].move_pattern & direction))
9254     TurnRound(newx, newy);
9255
9256   /* prevent elements on conveyor belt from moving on in last direction */
9257   if (pushed_by_conveyor && CAN_FALL(element) &&
9258       direction & MV_HORIZONTAL)
9259     MovDir[newx][newy] = 0;
9260
9261   if (!pushed_by_player)
9262   {
9263     int nextx = newx + dx, nexty = newy + dy;
9264     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9265
9266     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9267
9268     if (CAN_FALL(element) && direction == MV_DOWN)
9269       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9270
9271     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9272       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9273
9274 #if USE_FIX_IMPACT_COLLISION
9275     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9276       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9277 #endif
9278   }
9279
9280   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9281   {
9282     TestIfBadThingTouchesPlayer(newx, newy);
9283     TestIfBadThingTouchesFriend(newx, newy);
9284
9285     if (!IS_CUSTOM_ELEMENT(element))
9286       TestIfBadThingTouchesOtherBadThing(newx, newy);
9287   }
9288   else if (element == EL_PENGUIN)
9289     TestIfFriendTouchesBadThing(newx, newy);
9290
9291   if (DONT_GET_HIT_BY(element))
9292   {
9293     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9294   }
9295
9296   /* give the player one last chance (one more frame) to move away */
9297   if (CAN_FALL(element) && direction == MV_DOWN &&
9298       (last_line || (!IS_FREE(x, newy + 1) &&
9299                      (!IS_PLAYER(x, newy + 1) ||
9300                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9301     Impact(x, newy);
9302
9303   if (pushed_by_player && !game.use_change_when_pushing_bug)
9304   {
9305     int push_side = MV_DIR_OPPOSITE(direction);
9306     struct PlayerInfo *player = PLAYERINFO(x, y);
9307
9308     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9309                                player->index_bit, push_side);
9310     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9311                                         player->index_bit, push_side);
9312   }
9313
9314   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9315     MovDelay[newx][newy] = 1;
9316
9317   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9318
9319   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9320
9321 #if 0
9322   if (ChangePage[newx][newy] != -1)             /* delayed change */
9323   {
9324     int page = ChangePage[newx][newy];
9325     struct ElementChangeInfo *change = &ei->change_page[page];
9326
9327     ChangePage[newx][newy] = -1;
9328
9329     if (change->can_change)
9330     {
9331       if (ChangeElement(newx, newy, element, page))
9332       {
9333         if (change->post_change_function)
9334           change->post_change_function(newx, newy);
9335       }
9336     }
9337
9338     if (change->has_action)
9339       ExecuteCustomElementAction(newx, newy, element, page);
9340   }
9341 #endif
9342
9343   TestIfElementHitsCustomElement(newx, newy, direction);
9344   TestIfPlayerTouchesCustomElement(newx, newy);
9345   TestIfElementTouchesCustomElement(newx, newy);
9346
9347   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9348       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9349     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9350                              MV_DIR_OPPOSITE(direction));
9351 }
9352
9353 int AmoebeNachbarNr(int ax, int ay)
9354 {
9355   int i;
9356   int element = Feld[ax][ay];
9357   int group_nr = 0;
9358   static int xy[4][2] =
9359   {
9360     { 0, -1 },
9361     { -1, 0 },
9362     { +1, 0 },
9363     { 0, +1 }
9364   };
9365
9366   for (i = 0; i < NUM_DIRECTIONS; i++)
9367   {
9368     int x = ax + xy[i][0];
9369     int y = ay + xy[i][1];
9370
9371     if (!IN_LEV_FIELD(x, y))
9372       continue;
9373
9374     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9375       group_nr = AmoebaNr[x][y];
9376   }
9377
9378   return group_nr;
9379 }
9380
9381 void AmoebenVereinigen(int ax, int ay)
9382 {
9383   int i, x, y, xx, yy;
9384   int new_group_nr = AmoebaNr[ax][ay];
9385   static int xy[4][2] =
9386   {
9387     { 0, -1 },
9388     { -1, 0 },
9389     { +1, 0 },
9390     { 0, +1 }
9391   };
9392
9393   if (new_group_nr == 0)
9394     return;
9395
9396   for (i = 0; i < NUM_DIRECTIONS; i++)
9397   {
9398     x = ax + xy[i][0];
9399     y = ay + xy[i][1];
9400
9401     if (!IN_LEV_FIELD(x, y))
9402       continue;
9403
9404     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9405          Feld[x][y] == EL_BD_AMOEBA ||
9406          Feld[x][y] == EL_AMOEBA_DEAD) &&
9407         AmoebaNr[x][y] != new_group_nr)
9408     {
9409       int old_group_nr = AmoebaNr[x][y];
9410
9411       if (old_group_nr == 0)
9412         return;
9413
9414       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9415       AmoebaCnt[old_group_nr] = 0;
9416       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9417       AmoebaCnt2[old_group_nr] = 0;
9418
9419       SCAN_PLAYFIELD(xx, yy)
9420       {
9421         if (AmoebaNr[xx][yy] == old_group_nr)
9422           AmoebaNr[xx][yy] = new_group_nr;
9423       }
9424     }
9425   }
9426 }
9427
9428 void AmoebeUmwandeln(int ax, int ay)
9429 {
9430   int i, x, y;
9431
9432   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9433   {
9434     int group_nr = AmoebaNr[ax][ay];
9435
9436 #ifdef DEBUG
9437     if (group_nr == 0)
9438     {
9439       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9440       printf("AmoebeUmwandeln(): This should never happen!\n");
9441       return;
9442     }
9443 #endif
9444
9445     SCAN_PLAYFIELD(x, y)
9446     {
9447       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9448       {
9449         AmoebaNr[x][y] = 0;
9450         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9451       }
9452     }
9453
9454     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9455                             SND_AMOEBA_TURNING_TO_GEM :
9456                             SND_AMOEBA_TURNING_TO_ROCK));
9457     Bang(ax, ay);
9458   }
9459   else
9460   {
9461     static int xy[4][2] =
9462     {
9463       { 0, -1 },
9464       { -1, 0 },
9465       { +1, 0 },
9466       { 0, +1 }
9467     };
9468
9469     for (i = 0; i < NUM_DIRECTIONS; i++)
9470     {
9471       x = ax + xy[i][0];
9472       y = ay + xy[i][1];
9473
9474       if (!IN_LEV_FIELD(x, y))
9475         continue;
9476
9477       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9478       {
9479         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9480                               SND_AMOEBA_TURNING_TO_GEM :
9481                               SND_AMOEBA_TURNING_TO_ROCK));
9482         Bang(x, y);
9483       }
9484     }
9485   }
9486 }
9487
9488 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9489 {
9490   int x, y;
9491   int group_nr = AmoebaNr[ax][ay];
9492   boolean done = FALSE;
9493
9494 #ifdef DEBUG
9495   if (group_nr == 0)
9496   {
9497     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9498     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9499     return;
9500   }
9501 #endif
9502
9503   SCAN_PLAYFIELD(x, y)
9504   {
9505     if (AmoebaNr[x][y] == group_nr &&
9506         (Feld[x][y] == EL_AMOEBA_DEAD ||
9507          Feld[x][y] == EL_BD_AMOEBA ||
9508          Feld[x][y] == EL_AMOEBA_GROWING))
9509     {
9510       AmoebaNr[x][y] = 0;
9511       Feld[x][y] = new_element;
9512       InitField(x, y, FALSE);
9513       TEST_DrawLevelField(x, y);
9514       done = TRUE;
9515     }
9516   }
9517
9518   if (done)
9519     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9520                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9521                             SND_BD_AMOEBA_TURNING_TO_GEM));
9522 }
9523
9524 void AmoebeWaechst(int x, int y)
9525 {
9526   static unsigned long sound_delay = 0;
9527   static unsigned long sound_delay_value = 0;
9528
9529   if (!MovDelay[x][y])          /* start new growing cycle */
9530   {
9531     MovDelay[x][y] = 7;
9532
9533     if (DelayReached(&sound_delay, sound_delay_value))
9534     {
9535       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9536       sound_delay_value = 30;
9537     }
9538   }
9539
9540   if (MovDelay[x][y])           /* wait some time before growing bigger */
9541   {
9542     MovDelay[x][y]--;
9543     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9544     {
9545       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9546                                            6 - MovDelay[x][y]);
9547
9548       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9549     }
9550
9551     if (!MovDelay[x][y])
9552     {
9553       Feld[x][y] = Store[x][y];
9554       Store[x][y] = 0;
9555       TEST_DrawLevelField(x, y);
9556     }
9557   }
9558 }
9559
9560 void AmoebaDisappearing(int x, int y)
9561 {
9562   static unsigned long sound_delay = 0;
9563   static unsigned long sound_delay_value = 0;
9564
9565   if (!MovDelay[x][y])          /* start new shrinking cycle */
9566   {
9567     MovDelay[x][y] = 7;
9568
9569     if (DelayReached(&sound_delay, sound_delay_value))
9570       sound_delay_value = 30;
9571   }
9572
9573   if (MovDelay[x][y])           /* wait some time before shrinking */
9574   {
9575     MovDelay[x][y]--;
9576     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9577     {
9578       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9579                                            6 - MovDelay[x][y]);
9580
9581       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9582     }
9583
9584     if (!MovDelay[x][y])
9585     {
9586       Feld[x][y] = EL_EMPTY;
9587       TEST_DrawLevelField(x, y);
9588
9589       /* don't let mole enter this field in this cycle;
9590          (give priority to objects falling to this field from above) */
9591       Stop[x][y] = TRUE;
9592     }
9593   }
9594 }
9595
9596 void AmoebeAbleger(int ax, int ay)
9597 {
9598   int i;
9599   int element = Feld[ax][ay];
9600   int graphic = el2img(element);
9601   int newax = ax, neway = ay;
9602   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9603   static int xy[4][2] =
9604   {
9605     { 0, -1 },
9606     { -1, 0 },
9607     { +1, 0 },
9608     { 0, +1 }
9609   };
9610
9611   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9612   {
9613     Feld[ax][ay] = EL_AMOEBA_DEAD;
9614     TEST_DrawLevelField(ax, ay);
9615     return;
9616   }
9617
9618   if (IS_ANIMATED(graphic))
9619     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9620
9621   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9622     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9623
9624   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9625   {
9626     MovDelay[ax][ay]--;
9627     if (MovDelay[ax][ay])
9628       return;
9629   }
9630
9631   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9632   {
9633     int start = RND(4);
9634     int x = ax + xy[start][0];
9635     int y = ay + xy[start][1];
9636
9637     if (!IN_LEV_FIELD(x, y))
9638       return;
9639
9640     if (IS_FREE(x, y) ||
9641         CAN_GROW_INTO(Feld[x][y]) ||
9642         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9643         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9644     {
9645       newax = x;
9646       neway = y;
9647     }
9648
9649     if (newax == ax && neway == ay)
9650       return;
9651   }
9652   else                          /* normal or "filled" (BD style) amoeba */
9653   {
9654     int start = RND(4);
9655     boolean waiting_for_player = FALSE;
9656
9657     for (i = 0; i < NUM_DIRECTIONS; i++)
9658     {
9659       int j = (start + i) % 4;
9660       int x = ax + xy[j][0];
9661       int y = ay + xy[j][1];
9662
9663       if (!IN_LEV_FIELD(x, y))
9664         continue;
9665
9666       if (IS_FREE(x, y) ||
9667           CAN_GROW_INTO(Feld[x][y]) ||
9668           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9669           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9670       {
9671         newax = x;
9672         neway = y;
9673         break;
9674       }
9675       else if (IS_PLAYER(x, y))
9676         waiting_for_player = TRUE;
9677     }
9678
9679     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9680     {
9681       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9682       {
9683         Feld[ax][ay] = EL_AMOEBA_DEAD;
9684         TEST_DrawLevelField(ax, ay);
9685         AmoebaCnt[AmoebaNr[ax][ay]]--;
9686
9687         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9688         {
9689           if (element == EL_AMOEBA_FULL)
9690             AmoebeUmwandeln(ax, ay);
9691           else if (element == EL_BD_AMOEBA)
9692             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9693         }
9694       }
9695       return;
9696     }
9697     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9698     {
9699       /* amoeba gets larger by growing in some direction */
9700
9701       int new_group_nr = AmoebaNr[ax][ay];
9702
9703 #ifdef DEBUG
9704   if (new_group_nr == 0)
9705   {
9706     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9707     printf("AmoebeAbleger(): This should never happen!\n");
9708     return;
9709   }
9710 #endif
9711
9712       AmoebaNr[newax][neway] = new_group_nr;
9713       AmoebaCnt[new_group_nr]++;
9714       AmoebaCnt2[new_group_nr]++;
9715
9716       /* if amoeba touches other amoeba(s) after growing, unify them */
9717       AmoebenVereinigen(newax, neway);
9718
9719       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9720       {
9721         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9722         return;
9723       }
9724     }
9725   }
9726
9727   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9728       (neway == lev_fieldy - 1 && newax != ax))
9729   {
9730     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9731     Store[newax][neway] = element;
9732   }
9733   else if (neway == ay || element == EL_EMC_DRIPPER)
9734   {
9735     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9736
9737     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9738   }
9739   else
9740   {
9741     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9742     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9743     Store[ax][ay] = EL_AMOEBA_DROP;
9744     ContinueMoving(ax, ay);
9745     return;
9746   }
9747
9748   TEST_DrawLevelField(newax, neway);
9749 }
9750
9751 void Life(int ax, int ay)
9752 {
9753   int x1, y1, x2, y2;
9754   int life_time = 40;
9755   int element = Feld[ax][ay];
9756   int graphic = el2img(element);
9757   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9758                          level.biomaze);
9759   boolean changed = FALSE;
9760
9761   if (IS_ANIMATED(graphic))
9762     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9763
9764   if (Stop[ax][ay])
9765     return;
9766
9767   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9768     MovDelay[ax][ay] = life_time;
9769
9770   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9771   {
9772     MovDelay[ax][ay]--;
9773     if (MovDelay[ax][ay])
9774       return;
9775   }
9776
9777   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9778   {
9779     int xx = ax+x1, yy = ay+y1;
9780     int nachbarn = 0;
9781
9782     if (!IN_LEV_FIELD(xx, yy))
9783       continue;
9784
9785     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9786     {
9787       int x = xx+x2, y = yy+y2;
9788
9789       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9790         continue;
9791
9792       if (((Feld[x][y] == element ||
9793             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9794            !Stop[x][y]) ||
9795           (IS_FREE(x, y) && Stop[x][y]))
9796         nachbarn++;
9797     }
9798
9799     if (xx == ax && yy == ay)           /* field in the middle */
9800     {
9801       if (nachbarn < life_parameter[0] ||
9802           nachbarn > life_parameter[1])
9803       {
9804         Feld[xx][yy] = EL_EMPTY;
9805         if (!Stop[xx][yy])
9806           TEST_DrawLevelField(xx, yy);
9807         Stop[xx][yy] = TRUE;
9808         changed = TRUE;
9809       }
9810     }
9811     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9812     {                                   /* free border field */
9813       if (nachbarn >= life_parameter[2] &&
9814           nachbarn <= life_parameter[3])
9815       {
9816         Feld[xx][yy] = element;
9817         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9818         if (!Stop[xx][yy])
9819           TEST_DrawLevelField(xx, yy);
9820         Stop[xx][yy] = TRUE;
9821         changed = TRUE;
9822       }
9823     }
9824   }
9825
9826   if (changed)
9827     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9828                    SND_GAME_OF_LIFE_GROWING);
9829 }
9830
9831 static void InitRobotWheel(int x, int y)
9832 {
9833   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9834 }
9835
9836 static void RunRobotWheel(int x, int y)
9837 {
9838   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9839 }
9840
9841 static void StopRobotWheel(int x, int y)
9842 {
9843   if (ZX == x && ZY == y)
9844   {
9845     ZX = ZY = -1;
9846
9847     game.robot_wheel_active = FALSE;
9848   }
9849 }
9850
9851 static void InitTimegateWheel(int x, int y)
9852 {
9853   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9854 }
9855
9856 static void RunTimegateWheel(int x, int y)
9857 {
9858   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9859 }
9860
9861 static void InitMagicBallDelay(int x, int y)
9862 {
9863 #if 1
9864   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9865 #else
9866   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9867 #endif
9868 }
9869
9870 static void ActivateMagicBall(int bx, int by)
9871 {
9872   int x, y;
9873
9874   if (level.ball_random)
9875   {
9876     int pos_border = RND(8);    /* select one of the eight border elements */
9877     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9878     int xx = pos_content % 3;
9879     int yy = pos_content / 3;
9880
9881     x = bx - 1 + xx;
9882     y = by - 1 + yy;
9883
9884     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9885       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9886   }
9887   else
9888   {
9889     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9890     {
9891       int xx = x - bx + 1;
9892       int yy = y - by + 1;
9893
9894       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9895         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9896     }
9897   }
9898
9899   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9900 }
9901
9902 void CheckExit(int x, int y)
9903 {
9904   if (local_player->gems_still_needed > 0 ||
9905       local_player->sokobanfields_still_needed > 0 ||
9906       local_player->lights_still_needed > 0)
9907   {
9908     int element = Feld[x][y];
9909     int graphic = el2img(element);
9910
9911     if (IS_ANIMATED(graphic))
9912       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9913
9914     return;
9915   }
9916
9917   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9918     return;
9919
9920   Feld[x][y] = EL_EXIT_OPENING;
9921
9922   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9923 }
9924
9925 void CheckExitEM(int x, int y)
9926 {
9927   if (local_player->gems_still_needed > 0 ||
9928       local_player->sokobanfields_still_needed > 0 ||
9929       local_player->lights_still_needed > 0)
9930   {
9931     int element = Feld[x][y];
9932     int graphic = el2img(element);
9933
9934     if (IS_ANIMATED(graphic))
9935       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9936
9937     return;
9938   }
9939
9940   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9941     return;
9942
9943   Feld[x][y] = EL_EM_EXIT_OPENING;
9944
9945   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9946 }
9947
9948 void CheckExitSteel(int x, int y)
9949 {
9950   if (local_player->gems_still_needed > 0 ||
9951       local_player->sokobanfields_still_needed > 0 ||
9952       local_player->lights_still_needed > 0)
9953   {
9954     int element = Feld[x][y];
9955     int graphic = el2img(element);
9956
9957     if (IS_ANIMATED(graphic))
9958       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9959
9960     return;
9961   }
9962
9963   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9964     return;
9965
9966   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9967
9968   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9969 }
9970
9971 void CheckExitSteelEM(int x, int y)
9972 {
9973   if (local_player->gems_still_needed > 0 ||
9974       local_player->sokobanfields_still_needed > 0 ||
9975       local_player->lights_still_needed > 0)
9976   {
9977     int element = Feld[x][y];
9978     int graphic = el2img(element);
9979
9980     if (IS_ANIMATED(graphic))
9981       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9982
9983     return;
9984   }
9985
9986   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9987     return;
9988
9989   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9990
9991   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9992 }
9993
9994 void CheckExitSP(int x, int y)
9995 {
9996   if (local_player->gems_still_needed > 0)
9997   {
9998     int element = Feld[x][y];
9999     int graphic = el2img(element);
10000
10001     if (IS_ANIMATED(graphic))
10002       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10003
10004     return;
10005   }
10006
10007   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10008     return;
10009
10010   Feld[x][y] = EL_SP_EXIT_OPENING;
10011
10012   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10013 }
10014
10015 static void CloseAllOpenTimegates()
10016 {
10017   int x, y;
10018
10019   SCAN_PLAYFIELD(x, y)
10020   {
10021     int element = Feld[x][y];
10022
10023     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10024     {
10025       Feld[x][y] = EL_TIMEGATE_CLOSING;
10026
10027       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10028     }
10029   }
10030 }
10031
10032 void DrawTwinkleOnField(int x, int y)
10033 {
10034   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10035     return;
10036
10037   if (Feld[x][y] == EL_BD_DIAMOND)
10038     return;
10039
10040   if (MovDelay[x][y] == 0)      /* next animation frame */
10041     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10042
10043   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10044   {
10045     MovDelay[x][y]--;
10046
10047     DrawLevelElementAnimation(x, y, Feld[x][y]);
10048
10049     if (MovDelay[x][y] != 0)
10050     {
10051       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10052                                            10 - MovDelay[x][y]);
10053
10054       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10055     }
10056   }
10057 }
10058
10059 void MauerWaechst(int x, int y)
10060 {
10061   int delay = 6;
10062
10063   if (!MovDelay[x][y])          /* next animation frame */
10064     MovDelay[x][y] = 3 * delay;
10065
10066   if (MovDelay[x][y])           /* wait some time before next frame */
10067   {
10068     MovDelay[x][y]--;
10069
10070     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10071     {
10072       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10073       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10074
10075       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10076     }
10077
10078     if (!MovDelay[x][y])
10079     {
10080       if (MovDir[x][y] == MV_LEFT)
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_RIGHT)
10086       {
10087         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10088           TEST_DrawLevelField(x + 1, y);
10089       }
10090       else if (MovDir[x][y] == MV_UP)
10091       {
10092         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10093           TEST_DrawLevelField(x, y - 1);
10094       }
10095       else
10096       {
10097         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10098           TEST_DrawLevelField(x, y + 1);
10099       }
10100
10101       Feld[x][y] = Store[x][y];
10102       Store[x][y] = 0;
10103       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10104       TEST_DrawLevelField(x, y);
10105     }
10106   }
10107 }
10108
10109 void MauerAbleger(int ax, int ay)
10110 {
10111   int element = Feld[ax][ay];
10112   int graphic = el2img(element);
10113   boolean oben_frei = FALSE, unten_frei = FALSE;
10114   boolean links_frei = FALSE, rechts_frei = FALSE;
10115   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10116   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10117   boolean new_wall = FALSE;
10118
10119   if (IS_ANIMATED(graphic))
10120     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10121
10122   if (!MovDelay[ax][ay])        /* start building new wall */
10123     MovDelay[ax][ay] = 6;
10124
10125   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10126   {
10127     MovDelay[ax][ay]--;
10128     if (MovDelay[ax][ay])
10129       return;
10130   }
10131
10132   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10133     oben_frei = TRUE;
10134   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10135     unten_frei = TRUE;
10136   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10137     links_frei = TRUE;
10138   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10139     rechts_frei = TRUE;
10140
10141   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10142       element == EL_EXPANDABLE_WALL_ANY)
10143   {
10144     if (oben_frei)
10145     {
10146       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10147       Store[ax][ay-1] = element;
10148       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10149       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10150         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10151                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10152       new_wall = TRUE;
10153     }
10154     if (unten_frei)
10155     {
10156       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10157       Store[ax][ay+1] = element;
10158       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10159       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10160         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10161                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10162       new_wall = TRUE;
10163     }
10164   }
10165
10166   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10167       element == EL_EXPANDABLE_WALL_ANY ||
10168       element == EL_EXPANDABLE_WALL ||
10169       element == EL_BD_EXPANDABLE_WALL)
10170   {
10171     if (links_frei)
10172     {
10173       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10174       Store[ax-1][ay] = element;
10175       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10176       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10177         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10178                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10179       new_wall = TRUE;
10180     }
10181
10182     if (rechts_frei)
10183     {
10184       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10185       Store[ax+1][ay] = element;
10186       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10187       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10188         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10189                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10190       new_wall = TRUE;
10191     }
10192   }
10193
10194   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10195     TEST_DrawLevelField(ax, ay);
10196
10197   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10198     oben_massiv = TRUE;
10199   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10200     unten_massiv = TRUE;
10201   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10202     links_massiv = TRUE;
10203   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10204     rechts_massiv = TRUE;
10205
10206   if (((oben_massiv && unten_massiv) ||
10207        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10208        element == EL_EXPANDABLE_WALL) &&
10209       ((links_massiv && rechts_massiv) ||
10210        element == EL_EXPANDABLE_WALL_VERTICAL))
10211     Feld[ax][ay] = EL_WALL;
10212
10213   if (new_wall)
10214     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10215 }
10216
10217 void MauerAblegerStahl(int ax, int ay)
10218 {
10219   int element = Feld[ax][ay];
10220   int graphic = el2img(element);
10221   boolean oben_frei = FALSE, unten_frei = FALSE;
10222   boolean links_frei = FALSE, rechts_frei = FALSE;
10223   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10224   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10225   boolean new_wall = FALSE;
10226
10227   if (IS_ANIMATED(graphic))
10228     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10229
10230   if (!MovDelay[ax][ay])        /* start building new wall */
10231     MovDelay[ax][ay] = 6;
10232
10233   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10234   {
10235     MovDelay[ax][ay]--;
10236     if (MovDelay[ax][ay])
10237       return;
10238   }
10239
10240   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10241     oben_frei = TRUE;
10242   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10243     unten_frei = TRUE;
10244   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10245     links_frei = TRUE;
10246   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10247     rechts_frei = TRUE;
10248
10249   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10250       element == EL_EXPANDABLE_STEELWALL_ANY)
10251   {
10252     if (oben_frei)
10253     {
10254       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10255       Store[ax][ay-1] = element;
10256       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10257       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10258         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10259                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10260       new_wall = TRUE;
10261     }
10262     if (unten_frei)
10263     {
10264       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10265       Store[ax][ay+1] = element;
10266       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10267       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10268         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10269                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10270       new_wall = TRUE;
10271     }
10272   }
10273
10274   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10275       element == EL_EXPANDABLE_STEELWALL_ANY)
10276   {
10277     if (links_frei)
10278     {
10279       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10280       Store[ax-1][ay] = element;
10281       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10282       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10283         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10284                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10285       new_wall = TRUE;
10286     }
10287
10288     if (rechts_frei)
10289     {
10290       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10291       Store[ax+1][ay] = element;
10292       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10293       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10294         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10295                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10296       new_wall = TRUE;
10297     }
10298   }
10299
10300   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10301     oben_massiv = TRUE;
10302   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10303     unten_massiv = TRUE;
10304   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10305     links_massiv = TRUE;
10306   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10307     rechts_massiv = TRUE;
10308
10309   if (((oben_massiv && unten_massiv) ||
10310        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10311       ((links_massiv && rechts_massiv) ||
10312        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10313     Feld[ax][ay] = EL_STEELWALL;
10314
10315   if (new_wall)
10316     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10317 }
10318
10319 void CheckForDragon(int x, int y)
10320 {
10321   int i, j;
10322   boolean dragon_found = FALSE;
10323   static int xy[4][2] =
10324   {
10325     { 0, -1 },
10326     { -1, 0 },
10327     { +1, 0 },
10328     { 0, +1 }
10329   };
10330
10331   for (i = 0; i < NUM_DIRECTIONS; i++)
10332   {
10333     for (j = 0; j < 4; j++)
10334     {
10335       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10336
10337       if (IN_LEV_FIELD(xx, yy) &&
10338           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10339       {
10340         if (Feld[xx][yy] == EL_DRAGON)
10341           dragon_found = TRUE;
10342       }
10343       else
10344         break;
10345     }
10346   }
10347
10348   if (!dragon_found)
10349   {
10350     for (i = 0; i < NUM_DIRECTIONS; i++)
10351     {
10352       for (j = 0; j < 3; j++)
10353       {
10354         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10355   
10356         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10357         {
10358           Feld[xx][yy] = EL_EMPTY;
10359           TEST_DrawLevelField(xx, yy);
10360         }
10361         else
10362           break;
10363       }
10364     }
10365   }
10366 }
10367
10368 static void InitBuggyBase(int x, int y)
10369 {
10370   int element = Feld[x][y];
10371   int activating_delay = FRAMES_PER_SECOND / 4;
10372
10373   ChangeDelay[x][y] =
10374     (element == EL_SP_BUGGY_BASE ?
10375      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10376      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10377      activating_delay :
10378      element == EL_SP_BUGGY_BASE_ACTIVE ?
10379      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10380 }
10381
10382 static void WarnBuggyBase(int x, int y)
10383 {
10384   int i;
10385   static int xy[4][2] =
10386   {
10387     { 0, -1 },
10388     { -1, 0 },
10389     { +1, 0 },
10390     { 0, +1 }
10391   };
10392
10393   for (i = 0; i < NUM_DIRECTIONS; i++)
10394   {
10395     int xx = x + xy[i][0];
10396     int yy = y + xy[i][1];
10397
10398     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10399     {
10400       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10401
10402       break;
10403     }
10404   }
10405 }
10406
10407 static void InitTrap(int x, int y)
10408 {
10409   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10410 }
10411
10412 static void ActivateTrap(int x, int y)
10413 {
10414   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10415 }
10416
10417 static void ChangeActiveTrap(int x, int y)
10418 {
10419   int graphic = IMG_TRAP_ACTIVE;
10420
10421   /* if new animation frame was drawn, correct crumbled sand border */
10422   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10423     TEST_DrawLevelFieldCrumbled(x, y);
10424 }
10425
10426 static int getSpecialActionElement(int element, int number, int base_element)
10427 {
10428   return (element != EL_EMPTY ? element :
10429           number != -1 ? base_element + number - 1 :
10430           EL_EMPTY);
10431 }
10432
10433 static int getModifiedActionNumber(int value_old, int operator, int operand,
10434                                    int value_min, int value_max)
10435 {
10436   int value_new = (operator == CA_MODE_SET      ? operand :
10437                    operator == CA_MODE_ADD      ? value_old + operand :
10438                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10439                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10440                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10441                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10442                    value_old);
10443
10444   return (value_new < value_min ? value_min :
10445           value_new > value_max ? value_max :
10446           value_new);
10447 }
10448
10449 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10450 {
10451   struct ElementInfo *ei = &element_info[element];
10452   struct ElementChangeInfo *change = &ei->change_page[page];
10453   int target_element = change->target_element;
10454   int action_type = change->action_type;
10455   int action_mode = change->action_mode;
10456   int action_arg = change->action_arg;
10457   int action_element = change->action_element;
10458   int i;
10459
10460   if (!change->has_action)
10461     return;
10462
10463   /* ---------- determine action paramater values -------------------------- */
10464
10465   int level_time_value =
10466     (level.time > 0 ? TimeLeft :
10467      TimePlayed);
10468
10469   int action_arg_element_raw =
10470     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10471      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10472      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10473      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10474      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10475      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10476      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10477      EL_EMPTY);
10478   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10479
10480 #if 0
10481   if (action_arg_element_raw == EL_GROUP_START)
10482     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10483 #endif
10484
10485   int action_arg_direction =
10486     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10487      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10488      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10489      change->actual_trigger_side :
10490      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10491      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10492      MV_NONE);
10493
10494   int action_arg_number_min =
10495     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10496      CA_ARG_MIN);
10497
10498   int action_arg_number_max =
10499     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10500      action_type == CA_SET_LEVEL_GEMS ? 999 :
10501      action_type == CA_SET_LEVEL_TIME ? 9999 :
10502      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10503      action_type == CA_SET_CE_VALUE ? 9999 :
10504      action_type == CA_SET_CE_SCORE ? 9999 :
10505      CA_ARG_MAX);
10506
10507   int action_arg_number_reset =
10508     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10509      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10510      action_type == CA_SET_LEVEL_TIME ? level.time :
10511      action_type == CA_SET_LEVEL_SCORE ? 0 :
10512 #if USE_NEW_CUSTOM_VALUE
10513      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10514 #else
10515      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10516 #endif
10517      action_type == CA_SET_CE_SCORE ? 0 :
10518      0);
10519
10520   int action_arg_number =
10521     (action_arg <= CA_ARG_MAX ? action_arg :
10522      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10523      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10524      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10525      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10526      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10527      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10528 #if USE_NEW_CUSTOM_VALUE
10529      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10530 #else
10531      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10532 #endif
10533      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10534      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10535      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10536      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10537      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10538      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10539      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10540      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10541      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10542      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10543      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10544      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10545      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10546      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10547      -1);
10548
10549   int action_arg_number_old =
10550     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10551      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10552      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10553      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10554      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10555      0);
10556
10557   int action_arg_number_new =
10558     getModifiedActionNumber(action_arg_number_old,
10559                             action_mode, action_arg_number,
10560                             action_arg_number_min, action_arg_number_max);
10561
10562 #if 1
10563   int trigger_player_bits =
10564     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10565      change->actual_trigger_player_bits : change->trigger_player);
10566 #else
10567   int trigger_player_bits =
10568     (change->actual_trigger_player >= EL_PLAYER_1 &&
10569      change->actual_trigger_player <= EL_PLAYER_4 ?
10570      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10571      PLAYER_BITS_ANY);
10572 #endif
10573
10574   int action_arg_player_bits =
10575     (action_arg >= CA_ARG_PLAYER_1 &&
10576      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10577      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10578      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10579      PLAYER_BITS_ANY);
10580
10581   /* ---------- execute action  -------------------------------------------- */
10582
10583   switch (action_type)
10584   {
10585     case CA_NO_ACTION:
10586     {
10587       return;
10588     }
10589
10590     /* ---------- level actions  ------------------------------------------- */
10591
10592     case CA_RESTART_LEVEL:
10593     {
10594       game.restart_level = TRUE;
10595
10596       break;
10597     }
10598
10599     case CA_SHOW_ENVELOPE:
10600     {
10601       int element = getSpecialActionElement(action_arg_element,
10602                                             action_arg_number, EL_ENVELOPE_1);
10603
10604       if (IS_ENVELOPE(element))
10605         local_player->show_envelope = element;
10606
10607       break;
10608     }
10609
10610     case CA_SET_LEVEL_TIME:
10611     {
10612       if (level.time > 0)       /* only modify limited time value */
10613       {
10614         TimeLeft = action_arg_number_new;
10615
10616 #if 1
10617         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10618
10619         DisplayGameControlValues();
10620 #else
10621         DrawGameValue_Time(TimeLeft);
10622 #endif
10623
10624         if (!TimeLeft && setup.time_limit)
10625           for (i = 0; i < MAX_PLAYERS; i++)
10626             KillPlayer(&stored_player[i]);
10627       }
10628
10629       break;
10630     }
10631
10632     case CA_SET_LEVEL_SCORE:
10633     {
10634       local_player->score = action_arg_number_new;
10635
10636 #if 1
10637       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10638
10639       DisplayGameControlValues();
10640 #else
10641       DrawGameValue_Score(local_player->score);
10642 #endif
10643
10644       break;
10645     }
10646
10647     case CA_SET_LEVEL_GEMS:
10648     {
10649       local_player->gems_still_needed = action_arg_number_new;
10650
10651 #if 1
10652       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10653
10654       DisplayGameControlValues();
10655 #else
10656       DrawGameValue_Emeralds(local_player->gems_still_needed);
10657 #endif
10658
10659       break;
10660     }
10661
10662 #if !USE_PLAYER_GRAVITY
10663     case CA_SET_LEVEL_GRAVITY:
10664     {
10665       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10666                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10667                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10668                       game.gravity);
10669       break;
10670     }
10671 #endif
10672
10673     case CA_SET_LEVEL_WIND:
10674     {
10675       game.wind_direction = action_arg_direction;
10676
10677       break;
10678     }
10679
10680     case CA_SET_LEVEL_RANDOM_SEED:
10681     {
10682 #if 1
10683       /* ensure that setting a new random seed while playing is predictable */
10684       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10685 #else
10686       InitRND(action_arg_number_new);
10687 #endif
10688
10689 #if 0
10690       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10691 #endif
10692
10693 #if 0
10694       {
10695         int i;
10696
10697         printf("::: ");
10698         for (i = 0; i < 9; i++)
10699           printf("%d, ", RND(2));
10700         printf("\n");
10701       }
10702 #endif
10703
10704       break;
10705     }
10706
10707     /* ---------- player actions  ------------------------------------------ */
10708
10709     case CA_MOVE_PLAYER:
10710     {
10711       /* automatically move to the next field in specified direction */
10712       for (i = 0; i < MAX_PLAYERS; i++)
10713         if (trigger_player_bits & (1 << i))
10714           stored_player[i].programmed_action = action_arg_direction;
10715
10716       break;
10717     }
10718
10719     case CA_EXIT_PLAYER:
10720     {
10721       for (i = 0; i < MAX_PLAYERS; i++)
10722         if (action_arg_player_bits & (1 << i))
10723           PlayerWins(&stored_player[i]);
10724
10725       break;
10726     }
10727
10728     case CA_KILL_PLAYER:
10729     {
10730       for (i = 0; i < MAX_PLAYERS; i++)
10731         if (action_arg_player_bits & (1 << i))
10732           KillPlayer(&stored_player[i]);
10733
10734       break;
10735     }
10736
10737     case CA_SET_PLAYER_KEYS:
10738     {
10739       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10740       int element = getSpecialActionElement(action_arg_element,
10741                                             action_arg_number, EL_KEY_1);
10742
10743       if (IS_KEY(element))
10744       {
10745         for (i = 0; i < MAX_PLAYERS; i++)
10746         {
10747           if (trigger_player_bits & (1 << i))
10748           {
10749             stored_player[i].key[KEY_NR(element)] = key_state;
10750
10751             DrawGameDoorValues();
10752           }
10753         }
10754       }
10755
10756       break;
10757     }
10758
10759     case CA_SET_PLAYER_SPEED:
10760     {
10761 #if 0
10762       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10763 #endif
10764
10765       for (i = 0; i < MAX_PLAYERS; i++)
10766       {
10767         if (trigger_player_bits & (1 << i))
10768         {
10769           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10770
10771           if (action_arg == CA_ARG_SPEED_FASTER &&
10772               stored_player[i].cannot_move)
10773           {
10774             action_arg_number = STEPSIZE_VERY_SLOW;
10775           }
10776           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10777                    action_arg == CA_ARG_SPEED_FASTER)
10778           {
10779             action_arg_number = 2;
10780             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10781                            CA_MODE_MULTIPLY);
10782           }
10783           else if (action_arg == CA_ARG_NUMBER_RESET)
10784           {
10785             action_arg_number = level.initial_player_stepsize[i];
10786           }
10787
10788           move_stepsize =
10789             getModifiedActionNumber(move_stepsize,
10790                                     action_mode,
10791                                     action_arg_number,
10792                                     action_arg_number_min,
10793                                     action_arg_number_max);
10794
10795           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10796         }
10797       }
10798
10799       break;
10800     }
10801
10802     case CA_SET_PLAYER_SHIELD:
10803     {
10804       for (i = 0; i < MAX_PLAYERS; i++)
10805       {
10806         if (trigger_player_bits & (1 << i))
10807         {
10808           if (action_arg == CA_ARG_SHIELD_OFF)
10809           {
10810             stored_player[i].shield_normal_time_left = 0;
10811             stored_player[i].shield_deadly_time_left = 0;
10812           }
10813           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10814           {
10815             stored_player[i].shield_normal_time_left = 999999;
10816           }
10817           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10818           {
10819             stored_player[i].shield_normal_time_left = 999999;
10820             stored_player[i].shield_deadly_time_left = 999999;
10821           }
10822         }
10823       }
10824
10825       break;
10826     }
10827
10828 #if USE_PLAYER_GRAVITY
10829     case CA_SET_PLAYER_GRAVITY:
10830     {
10831       for (i = 0; i < MAX_PLAYERS; i++)
10832       {
10833         if (trigger_player_bits & (1 << i))
10834         {
10835           stored_player[i].gravity =
10836             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10837              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10838              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10839              stored_player[i].gravity);
10840         }
10841       }
10842
10843       break;
10844     }
10845 #endif
10846
10847     case CA_SET_PLAYER_ARTWORK:
10848     {
10849       for (i = 0; i < MAX_PLAYERS; i++)
10850       {
10851         if (trigger_player_bits & (1 << i))
10852         {
10853           int artwork_element = action_arg_element;
10854
10855           if (action_arg == CA_ARG_ELEMENT_RESET)
10856             artwork_element =
10857               (level.use_artwork_element[i] ? level.artwork_element[i] :
10858                stored_player[i].element_nr);
10859
10860 #if USE_GFX_RESET_PLAYER_ARTWORK
10861           if (stored_player[i].artwork_element != artwork_element)
10862             stored_player[i].Frame = 0;
10863 #endif
10864
10865           stored_player[i].artwork_element = artwork_element;
10866
10867           SetPlayerWaiting(&stored_player[i], FALSE);
10868
10869           /* set number of special actions for bored and sleeping animation */
10870           stored_player[i].num_special_action_bored =
10871             get_num_special_action(artwork_element,
10872                                    ACTION_BORING_1, ACTION_BORING_LAST);
10873           stored_player[i].num_special_action_sleeping =
10874             get_num_special_action(artwork_element,
10875                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10876         }
10877       }
10878
10879       break;
10880     }
10881
10882     case CA_SET_PLAYER_INVENTORY:
10883     {
10884       for (i = 0; i < MAX_PLAYERS; i++)
10885       {
10886         struct PlayerInfo *player = &stored_player[i];
10887         int j, k;
10888
10889         if (trigger_player_bits & (1 << i))
10890         {
10891           int inventory_element = action_arg_element;
10892
10893           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10894               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10895               action_arg == CA_ARG_ELEMENT_ACTION)
10896           {
10897             int element = inventory_element;
10898             int collect_count = element_info[element].collect_count_initial;
10899
10900             if (!IS_CUSTOM_ELEMENT(element))
10901               collect_count = 1;
10902
10903             if (collect_count == 0)
10904               player->inventory_infinite_element = element;
10905             else
10906               for (k = 0; k < collect_count; k++)
10907                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10908                   player->inventory_element[player->inventory_size++] =
10909                     element;
10910           }
10911           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10912                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10913                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10914           {
10915             if (player->inventory_infinite_element != EL_UNDEFINED &&
10916                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10917                                      action_arg_element_raw))
10918               player->inventory_infinite_element = EL_UNDEFINED;
10919
10920             for (k = 0, j = 0; j < player->inventory_size; j++)
10921             {
10922               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10923                                         action_arg_element_raw))
10924                 player->inventory_element[k++] = player->inventory_element[j];
10925             }
10926
10927             player->inventory_size = k;
10928           }
10929           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10930           {
10931             if (player->inventory_size > 0)
10932             {
10933               for (j = 0; j < player->inventory_size - 1; j++)
10934                 player->inventory_element[j] = player->inventory_element[j + 1];
10935
10936               player->inventory_size--;
10937             }
10938           }
10939           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10940           {
10941             if (player->inventory_size > 0)
10942               player->inventory_size--;
10943           }
10944           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10945           {
10946             player->inventory_infinite_element = EL_UNDEFINED;
10947             player->inventory_size = 0;
10948           }
10949           else if (action_arg == CA_ARG_INVENTORY_RESET)
10950           {
10951             player->inventory_infinite_element = EL_UNDEFINED;
10952             player->inventory_size = 0;
10953
10954             if (level.use_initial_inventory[i])
10955             {
10956               for (j = 0; j < level.initial_inventory_size[i]; j++)
10957               {
10958                 int element = level.initial_inventory_content[i][j];
10959                 int collect_count = element_info[element].collect_count_initial;
10960
10961                 if (!IS_CUSTOM_ELEMENT(element))
10962                   collect_count = 1;
10963
10964                 if (collect_count == 0)
10965                   player->inventory_infinite_element = element;
10966                 else
10967                   for (k = 0; k < collect_count; k++)
10968                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10969                       player->inventory_element[player->inventory_size++] =
10970                         element;
10971               }
10972             }
10973           }
10974         }
10975       }
10976
10977       break;
10978     }
10979
10980     /* ---------- CE actions  ---------------------------------------------- */
10981
10982     case CA_SET_CE_VALUE:
10983     {
10984 #if USE_NEW_CUSTOM_VALUE
10985       int last_ce_value = CustomValue[x][y];
10986
10987       CustomValue[x][y] = action_arg_number_new;
10988
10989       if (CustomValue[x][y] != last_ce_value)
10990       {
10991         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10992         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10993
10994         if (CustomValue[x][y] == 0)
10995         {
10996           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10997           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10998         }
10999       }
11000 #endif
11001
11002       break;
11003     }
11004
11005     case CA_SET_CE_SCORE:
11006     {
11007 #if USE_NEW_CUSTOM_VALUE
11008       int last_ce_score = ei->collect_score;
11009
11010       ei->collect_score = action_arg_number_new;
11011
11012       if (ei->collect_score != last_ce_score)
11013       {
11014         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11015         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11016
11017         if (ei->collect_score == 0)
11018         {
11019           int xx, yy;
11020
11021           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11022           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11023
11024           /*
11025             This is a very special case that seems to be a mixture between
11026             CheckElementChange() and CheckTriggeredElementChange(): while
11027             the first one only affects single elements that are triggered
11028             directly, the second one affects multiple elements in the playfield
11029             that are triggered indirectly by another element. This is a third
11030             case: Changing the CE score always affects multiple identical CEs,
11031             so every affected CE must be checked, not only the single CE for
11032             which the CE score was changed in the first place (as every instance
11033             of that CE shares the same CE score, and therefore also can change)!
11034           */
11035           SCAN_PLAYFIELD(xx, yy)
11036           {
11037             if (Feld[xx][yy] == element)
11038               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11039                                  CE_SCORE_GETS_ZERO);
11040           }
11041         }
11042       }
11043 #endif
11044
11045       break;
11046     }
11047
11048     case CA_SET_CE_ARTWORK:
11049     {
11050       int artwork_element = action_arg_element;
11051       boolean reset_frame = FALSE;
11052       int xx, yy;
11053
11054       if (action_arg == CA_ARG_ELEMENT_RESET)
11055         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11056                            element);
11057
11058       if (ei->gfx_element != artwork_element)
11059         reset_frame = TRUE;
11060
11061       ei->gfx_element = artwork_element;
11062
11063       SCAN_PLAYFIELD(xx, yy)
11064       {
11065         if (Feld[xx][yy] == element)
11066         {
11067           if (reset_frame)
11068           {
11069             ResetGfxAnimation(xx, yy);
11070             ResetRandomAnimationValue(xx, yy);
11071           }
11072
11073           TEST_DrawLevelField(xx, yy);
11074         }
11075       }
11076
11077       break;
11078     }
11079
11080     /* ---------- engine actions  ------------------------------------------ */
11081
11082     case CA_SET_ENGINE_SCAN_MODE:
11083     {
11084       InitPlayfieldScanMode(action_arg);
11085
11086       break;
11087     }
11088
11089     default:
11090       break;
11091   }
11092 }
11093
11094 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11095 {
11096   int old_element = Feld[x][y];
11097   int new_element = GetElementFromGroupElement(element);
11098   int previous_move_direction = MovDir[x][y];
11099 #if USE_NEW_CUSTOM_VALUE
11100   int last_ce_value = CustomValue[x][y];
11101 #endif
11102   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11103   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11104   boolean add_player_onto_element = (new_element_is_player &&
11105 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11106                                      /* this breaks SnakeBite when a snake is
11107                                         halfway through a door that closes */
11108                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11109                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11110 #endif
11111                                      IS_WALKABLE(old_element));
11112
11113 #if 0
11114   /* check if element under the player changes from accessible to unaccessible
11115      (needed for special case of dropping element which then changes) */
11116   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11117       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11118   {
11119     Bang(x, y);
11120
11121     return;
11122   }
11123 #endif
11124
11125   if (!add_player_onto_element)
11126   {
11127     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11128       RemoveMovingField(x, y);
11129     else
11130       RemoveField(x, y);
11131
11132     Feld[x][y] = new_element;
11133
11134 #if !USE_GFX_RESET_GFX_ANIMATION
11135     ResetGfxAnimation(x, y);
11136     ResetRandomAnimationValue(x, y);
11137 #endif
11138
11139     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11140       MovDir[x][y] = previous_move_direction;
11141
11142 #if USE_NEW_CUSTOM_VALUE
11143     if (element_info[new_element].use_last_ce_value)
11144       CustomValue[x][y] = last_ce_value;
11145 #endif
11146
11147     InitField_WithBug1(x, y, FALSE);
11148
11149     new_element = Feld[x][y];   /* element may have changed */
11150
11151 #if USE_GFX_RESET_GFX_ANIMATION
11152     ResetGfxAnimation(x, y);
11153     ResetRandomAnimationValue(x, y);
11154 #endif
11155
11156     TEST_DrawLevelField(x, y);
11157
11158     if (GFX_CRUMBLED(new_element))
11159       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11160   }
11161
11162 #if 1
11163   /* check if element under the player changes from accessible to unaccessible
11164      (needed for special case of dropping element which then changes) */
11165   /* (must be checked after creating new element for walkable group elements) */
11166 #if USE_FIX_KILLED_BY_NON_WALKABLE
11167   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11168       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11169   {
11170     Bang(x, y);
11171
11172     return;
11173   }
11174 #else
11175   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11176       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11177   {
11178     Bang(x, y);
11179
11180     return;
11181   }
11182 #endif
11183 #endif
11184
11185   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11186   if (new_element_is_player)
11187     RelocatePlayer(x, y, new_element);
11188
11189   if (is_change)
11190     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11191
11192   TestIfBadThingTouchesPlayer(x, y);
11193   TestIfPlayerTouchesCustomElement(x, y);
11194   TestIfElementTouchesCustomElement(x, y);
11195 }
11196
11197 static void CreateField(int x, int y, int element)
11198 {
11199   CreateFieldExt(x, y, element, FALSE);
11200 }
11201
11202 static void CreateElementFromChange(int x, int y, int element)
11203 {
11204   element = GET_VALID_RUNTIME_ELEMENT(element);
11205
11206 #if USE_STOP_CHANGED_ELEMENTS
11207   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11208   {
11209     int old_element = Feld[x][y];
11210
11211     /* prevent changed element from moving in same engine frame
11212        unless both old and new element can either fall or move */
11213     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11214         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11215       Stop[x][y] = TRUE;
11216   }
11217 #endif
11218
11219   CreateFieldExt(x, y, element, TRUE);
11220 }
11221
11222 static boolean ChangeElement(int x, int y, int element, int page)
11223 {
11224   struct ElementInfo *ei = &element_info[element];
11225   struct ElementChangeInfo *change = &ei->change_page[page];
11226   int ce_value = CustomValue[x][y];
11227   int ce_score = ei->collect_score;
11228   int target_element;
11229   int old_element = Feld[x][y];
11230
11231   /* always use default change event to prevent running into a loop */
11232   if (ChangeEvent[x][y] == -1)
11233     ChangeEvent[x][y] = CE_DELAY;
11234
11235   if (ChangeEvent[x][y] == CE_DELAY)
11236   {
11237     /* reset actual trigger element, trigger player and action element */
11238     change->actual_trigger_element = EL_EMPTY;
11239     change->actual_trigger_player = EL_EMPTY;
11240     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11241     change->actual_trigger_side = CH_SIDE_NONE;
11242     change->actual_trigger_ce_value = 0;
11243     change->actual_trigger_ce_score = 0;
11244   }
11245
11246   /* do not change elements more than a specified maximum number of changes */
11247   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11248     return FALSE;
11249
11250   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11251
11252   if (change->explode)
11253   {
11254     Bang(x, y);
11255
11256     return TRUE;
11257   }
11258
11259   if (change->use_target_content)
11260   {
11261     boolean complete_replace = TRUE;
11262     boolean can_replace[3][3];
11263     int xx, yy;
11264
11265     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11266     {
11267       boolean is_empty;
11268       boolean is_walkable;
11269       boolean is_diggable;
11270       boolean is_collectible;
11271       boolean is_removable;
11272       boolean is_destructible;
11273       int ex = x + xx - 1;
11274       int ey = y + yy - 1;
11275       int content_element = change->target_content.e[xx][yy];
11276       int e;
11277
11278       can_replace[xx][yy] = TRUE;
11279
11280       if (ex == x && ey == y)   /* do not check changing element itself */
11281         continue;
11282
11283       if (content_element == EL_EMPTY_SPACE)
11284       {
11285         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11286
11287         continue;
11288       }
11289
11290       if (!IN_LEV_FIELD(ex, ey))
11291       {
11292         can_replace[xx][yy] = FALSE;
11293         complete_replace = FALSE;
11294
11295         continue;
11296       }
11297
11298       e = Feld[ex][ey];
11299
11300       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11301         e = MovingOrBlocked2Element(ex, ey);
11302
11303       is_empty = (IS_FREE(ex, ey) ||
11304                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11305
11306       is_walkable     = (is_empty || IS_WALKABLE(e));
11307       is_diggable     = (is_empty || IS_DIGGABLE(e));
11308       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11309       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11310       is_removable    = (is_diggable || is_collectible);
11311
11312       can_replace[xx][yy] =
11313         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11314           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11315           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11316           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11317           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11318           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11319          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11320
11321       if (!can_replace[xx][yy])
11322         complete_replace = FALSE;
11323     }
11324
11325     if (!change->only_if_complete || complete_replace)
11326     {
11327       boolean something_has_changed = FALSE;
11328
11329       if (change->only_if_complete && change->use_random_replace &&
11330           RND(100) < change->random_percentage)
11331         return FALSE;
11332
11333       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11334       {
11335         int ex = x + xx - 1;
11336         int ey = y + yy - 1;
11337         int content_element;
11338
11339         if (can_replace[xx][yy] && (!change->use_random_replace ||
11340                                     RND(100) < change->random_percentage))
11341         {
11342           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11343             RemoveMovingField(ex, ey);
11344
11345           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11346
11347           content_element = change->target_content.e[xx][yy];
11348           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11349                                               ce_value, ce_score);
11350
11351           CreateElementFromChange(ex, ey, target_element);
11352
11353           something_has_changed = TRUE;
11354
11355           /* for symmetry reasons, freeze newly created border elements */
11356           if (ex != x || ey != y)
11357             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11358         }
11359       }
11360
11361       if (something_has_changed)
11362       {
11363         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11364         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11365       }
11366     }
11367   }
11368   else
11369   {
11370     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11371                                         ce_value, ce_score);
11372
11373     if (element == EL_DIAGONAL_GROWING ||
11374         element == EL_DIAGONAL_SHRINKING)
11375     {
11376       target_element = Store[x][y];
11377
11378       Store[x][y] = EL_EMPTY;
11379     }
11380
11381     CreateElementFromChange(x, y, target_element);
11382
11383     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11384     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11385   }
11386
11387   /* this uses direct change before indirect change */
11388   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11389
11390   return TRUE;
11391 }
11392
11393 #if USE_NEW_DELAYED_ACTION
11394
11395 static void HandleElementChange(int x, int y, int page)
11396 {
11397   int element = MovingOrBlocked2Element(x, y);
11398   struct ElementInfo *ei = &element_info[element];
11399   struct ElementChangeInfo *change = &ei->change_page[page];
11400   boolean handle_action_before_change = FALSE;
11401
11402 #ifdef DEBUG
11403   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11404       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11405   {
11406     printf("\n\n");
11407     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11408            x, y, element, element_info[element].token_name);
11409     printf("HandleElementChange(): This should never happen!\n");
11410     printf("\n\n");
11411   }
11412 #endif
11413
11414   /* this can happen with classic bombs on walkable, changing elements */
11415   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11416   {
11417 #if 0
11418     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11419       ChangeDelay[x][y] = 0;
11420 #endif
11421
11422     return;
11423   }
11424
11425   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11426   {
11427     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11428
11429     if (change->can_change)
11430     {
11431 #if 1
11432       /* !!! not clear why graphic animation should be reset at all here !!! */
11433       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11434 #if USE_GFX_RESET_WHEN_NOT_MOVING
11435       /* when a custom element is about to change (for example by change delay),
11436          do not reset graphic animation when the custom element is moving */
11437       if (!IS_MOVING(x, y))
11438 #endif
11439       {
11440         ResetGfxAnimation(x, y);
11441         ResetRandomAnimationValue(x, y);
11442       }
11443 #endif
11444
11445       if (change->pre_change_function)
11446         change->pre_change_function(x, y);
11447     }
11448   }
11449
11450   ChangeDelay[x][y]--;
11451
11452   if (ChangeDelay[x][y] != 0)           /* continue element change */
11453   {
11454     if (change->can_change)
11455     {
11456       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11457
11458       if (IS_ANIMATED(graphic))
11459         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11460
11461       if (change->change_function)
11462         change->change_function(x, y);
11463     }
11464   }
11465   else                                  /* finish element change */
11466   {
11467     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11468     {
11469       page = ChangePage[x][y];
11470       ChangePage[x][y] = -1;
11471
11472       change = &ei->change_page[page];
11473     }
11474
11475     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11476     {
11477       ChangeDelay[x][y] = 1;            /* try change after next move step */
11478       ChangePage[x][y] = page;          /* remember page to use for change */
11479
11480       return;
11481     }
11482
11483 #if 1
11484     /* special case: set new level random seed before changing element */
11485     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11486       handle_action_before_change = TRUE;
11487
11488     if (change->has_action && handle_action_before_change)
11489       ExecuteCustomElementAction(x, y, element, page);
11490 #endif
11491
11492     if (change->can_change)
11493     {
11494       if (ChangeElement(x, y, element, page))
11495       {
11496         if (change->post_change_function)
11497           change->post_change_function(x, y);
11498       }
11499     }
11500
11501     if (change->has_action && !handle_action_before_change)
11502       ExecuteCustomElementAction(x, y, element, page);
11503   }
11504 }
11505
11506 #else
11507
11508 static void HandleElementChange(int x, int y, int page)
11509 {
11510   int element = MovingOrBlocked2Element(x, y);
11511   struct ElementInfo *ei = &element_info[element];
11512   struct ElementChangeInfo *change = &ei->change_page[page];
11513
11514 #ifdef DEBUG
11515   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11516   {
11517     printf("\n\n");
11518     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11519            x, y, element, element_info[element].token_name);
11520     printf("HandleElementChange(): This should never happen!\n");
11521     printf("\n\n");
11522   }
11523 #endif
11524
11525   /* this can happen with classic bombs on walkable, changing elements */
11526   if (!CAN_CHANGE(element))
11527   {
11528 #if 0
11529     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11530       ChangeDelay[x][y] = 0;
11531 #endif
11532
11533     return;
11534   }
11535
11536   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11537   {
11538     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11539
11540     ResetGfxAnimation(x, y);
11541     ResetRandomAnimationValue(x, y);
11542
11543     if (change->pre_change_function)
11544       change->pre_change_function(x, y);
11545   }
11546
11547   ChangeDelay[x][y]--;
11548
11549   if (ChangeDelay[x][y] != 0)           /* continue element change */
11550   {
11551     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11552
11553     if (IS_ANIMATED(graphic))
11554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11555
11556     if (change->change_function)
11557       change->change_function(x, y);
11558   }
11559   else                                  /* finish element change */
11560   {
11561     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11562     {
11563       page = ChangePage[x][y];
11564       ChangePage[x][y] = -1;
11565
11566       change = &ei->change_page[page];
11567     }
11568
11569     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11570     {
11571       ChangeDelay[x][y] = 1;            /* try change after next move step */
11572       ChangePage[x][y] = page;          /* remember page to use for change */
11573
11574       return;
11575     }
11576
11577     if (ChangeElement(x, y, element, page))
11578     {
11579       if (change->post_change_function)
11580         change->post_change_function(x, y);
11581     }
11582   }
11583 }
11584
11585 #endif
11586
11587 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11588                                               int trigger_element,
11589                                               int trigger_event,
11590                                               int trigger_player,
11591                                               int trigger_side,
11592                                               int trigger_page)
11593 {
11594   boolean change_done_any = FALSE;
11595   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11596   int i;
11597
11598   if (!(trigger_events[trigger_element][trigger_event]))
11599     return FALSE;
11600
11601 #if 0
11602   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11603          trigger_event, recursion_loop_depth, recursion_loop_detected,
11604          recursion_loop_element, EL_NAME(recursion_loop_element));
11605 #endif
11606
11607   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11608
11609   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11610   {
11611     int element = EL_CUSTOM_START + i;
11612     boolean change_done = FALSE;
11613     int p;
11614
11615     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11616         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11617       continue;
11618
11619     for (p = 0; p < element_info[element].num_change_pages; p++)
11620     {
11621       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11622
11623       if (change->can_change_or_has_action &&
11624           change->has_event[trigger_event] &&
11625           change->trigger_side & trigger_side &&
11626           change->trigger_player & trigger_player &&
11627           change->trigger_page & trigger_page_bits &&
11628           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11629       {
11630         change->actual_trigger_element = trigger_element;
11631         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11632         change->actual_trigger_player_bits = trigger_player;
11633         change->actual_trigger_side = trigger_side;
11634         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11635         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11636
11637 #if 0
11638         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11639                element, EL_NAME(element), p);
11640 #endif
11641
11642         if ((change->can_change && !change_done) || change->has_action)
11643         {
11644           int x, y;
11645
11646           SCAN_PLAYFIELD(x, y)
11647           {
11648             if (Feld[x][y] == element)
11649             {
11650               if (change->can_change && !change_done)
11651               {
11652 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11653                 /* if element already changed in this frame, not only prevent
11654                    another element change (checked in ChangeElement()), but
11655                    also prevent additional element actions for this element */
11656
11657                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11658                     !level.use_action_after_change_bug)
11659                   continue;
11660 #endif
11661
11662 #if 0
11663                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11664                        element, EL_NAME(element), p);
11665 #endif
11666
11667                 ChangeDelay[x][y] = 1;
11668                 ChangeEvent[x][y] = trigger_event;
11669
11670                 HandleElementChange(x, y, p);
11671               }
11672 #if USE_NEW_DELAYED_ACTION
11673               else if (change->has_action)
11674               {
11675 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11676                 /* if element already changed in this frame, not only prevent
11677                    another element change (checked in ChangeElement()), but
11678                    also prevent additional element actions for this element */
11679
11680                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11681                     !level.use_action_after_change_bug)
11682                   continue;
11683 #endif
11684
11685
11686 #if 0
11687                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11688                        element, EL_NAME(element), p);
11689 #endif
11690
11691                 ExecuteCustomElementAction(x, y, element, p);
11692                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11693               }
11694 #else
11695               if (change->has_action)
11696               {
11697                 ExecuteCustomElementAction(x, y, element, p);
11698                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11699               }
11700 #endif
11701             }
11702           }
11703
11704           if (change->can_change)
11705           {
11706             change_done = TRUE;
11707             change_done_any = TRUE;
11708
11709 #if 0
11710             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11711                    element, EL_NAME(element), p);
11712 #endif
11713
11714           }
11715         }
11716       }
11717     }
11718   }
11719
11720   RECURSION_LOOP_DETECTION_END();
11721
11722   return change_done_any;
11723 }
11724
11725 static boolean CheckElementChangeExt(int x, int y,
11726                                      int element,
11727                                      int trigger_element,
11728                                      int trigger_event,
11729                                      int trigger_player,
11730                                      int trigger_side)
11731 {
11732   boolean change_done = FALSE;
11733   int p;
11734
11735   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11736       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11737     return FALSE;
11738
11739   if (Feld[x][y] == EL_BLOCKED)
11740   {
11741     Blocked2Moving(x, y, &x, &y);
11742     element = Feld[x][y];
11743   }
11744
11745 #if 0
11746   /* check if element has already changed */
11747   if (Feld[x][y] != element)
11748     return FALSE;
11749 #else
11750   /* check if element has already changed or is about to change after moving */
11751   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11752        Feld[x][y] != element) ||
11753
11754       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11755        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11756         ChangePage[x][y] != -1)))
11757     return FALSE;
11758 #endif
11759
11760 #if 0
11761   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11762          trigger_event, recursion_loop_depth, recursion_loop_detected,
11763          recursion_loop_element, EL_NAME(recursion_loop_element));
11764 #endif
11765
11766   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11767
11768 #if 0
11769   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11770 #endif
11771
11772   for (p = 0; p < element_info[element].num_change_pages; p++)
11773   {
11774     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11775
11776     /* check trigger element for all events where the element that is checked
11777        for changing interacts with a directly adjacent element -- this is
11778        different to element changes that affect other elements to change on the
11779        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11780     boolean check_trigger_element =
11781       (trigger_event == CE_TOUCHING_X ||
11782        trigger_event == CE_HITTING_X ||
11783        trigger_event == CE_HIT_BY_X ||
11784 #if 1
11785        /* this one was forgotten until 3.2.3 */
11786        trigger_event == CE_DIGGING_X);
11787 #endif
11788
11789     if (change->can_change_or_has_action &&
11790         change->has_event[trigger_event] &&
11791         change->trigger_side & trigger_side &&
11792         change->trigger_player & trigger_player &&
11793         (!check_trigger_element ||
11794          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11795     {
11796       change->actual_trigger_element = trigger_element;
11797       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11798       change->actual_trigger_player_bits = trigger_player;
11799       change->actual_trigger_side = trigger_side;
11800       change->actual_trigger_ce_value = CustomValue[x][y];
11801       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11802
11803       /* special case: trigger element not at (x,y) position for some events */
11804       if (check_trigger_element)
11805       {
11806         static struct
11807         {
11808           int dx, dy;
11809         } move_xy[] =
11810           {
11811             {  0,  0 },
11812             { -1,  0 },
11813             { +1,  0 },
11814             {  0,  0 },
11815             {  0, -1 },
11816             {  0,  0 }, { 0, 0 }, { 0, 0 },
11817             {  0, +1 }
11818           };
11819
11820         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11821         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11822
11823         change->actual_trigger_ce_value = CustomValue[xx][yy];
11824         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11825       }
11826
11827       if (change->can_change && !change_done)
11828       {
11829         ChangeDelay[x][y] = 1;
11830         ChangeEvent[x][y] = trigger_event;
11831
11832         HandleElementChange(x, y, p);
11833
11834         change_done = TRUE;
11835       }
11836 #if USE_NEW_DELAYED_ACTION
11837       else if (change->has_action)
11838       {
11839         ExecuteCustomElementAction(x, y, element, p);
11840         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11841       }
11842 #else
11843       if (change->has_action)
11844       {
11845         ExecuteCustomElementAction(x, y, element, p);
11846         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11847       }
11848 #endif
11849     }
11850   }
11851
11852   RECURSION_LOOP_DETECTION_END();
11853
11854   return change_done;
11855 }
11856
11857 static void PlayPlayerSound(struct PlayerInfo *player)
11858 {
11859   int jx = player->jx, jy = player->jy;
11860   int sound_element = player->artwork_element;
11861   int last_action = player->last_action_waiting;
11862   int action = player->action_waiting;
11863
11864   if (player->is_waiting)
11865   {
11866     if (action != last_action)
11867       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11868     else
11869       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11870   }
11871   else
11872   {
11873     if (action != last_action)
11874       StopSound(element_info[sound_element].sound[last_action]);
11875
11876     if (last_action == ACTION_SLEEPING)
11877       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11878   }
11879 }
11880
11881 static void PlayAllPlayersSound()
11882 {
11883   int i;
11884
11885   for (i = 0; i < MAX_PLAYERS; i++)
11886     if (stored_player[i].active)
11887       PlayPlayerSound(&stored_player[i]);
11888 }
11889
11890 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11891 {
11892   boolean last_waiting = player->is_waiting;
11893   int move_dir = player->MovDir;
11894
11895   player->dir_waiting = move_dir;
11896   player->last_action_waiting = player->action_waiting;
11897
11898   if (is_waiting)
11899   {
11900     if (!last_waiting)          /* not waiting -> waiting */
11901     {
11902       player->is_waiting = TRUE;
11903
11904       player->frame_counter_bored =
11905         FrameCounter +
11906         game.player_boring_delay_fixed +
11907         GetSimpleRandom(game.player_boring_delay_random);
11908       player->frame_counter_sleeping =
11909         FrameCounter +
11910         game.player_sleeping_delay_fixed +
11911         GetSimpleRandom(game.player_sleeping_delay_random);
11912
11913       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11914     }
11915
11916     if (game.player_sleeping_delay_fixed +
11917         game.player_sleeping_delay_random > 0 &&
11918         player->anim_delay_counter == 0 &&
11919         player->post_delay_counter == 0 &&
11920         FrameCounter >= player->frame_counter_sleeping)
11921       player->is_sleeping = TRUE;
11922     else if (game.player_boring_delay_fixed +
11923              game.player_boring_delay_random > 0 &&
11924              FrameCounter >= player->frame_counter_bored)
11925       player->is_bored = TRUE;
11926
11927     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11928                               player->is_bored ? ACTION_BORING :
11929                               ACTION_WAITING);
11930
11931     if (player->is_sleeping && player->use_murphy)
11932     {
11933       /* special case for sleeping Murphy when leaning against non-free tile */
11934
11935       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11936           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11937            !IS_MOVING(player->jx - 1, player->jy)))
11938         move_dir = MV_LEFT;
11939       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11940                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11941                 !IS_MOVING(player->jx + 1, player->jy)))
11942         move_dir = MV_RIGHT;
11943       else
11944         player->is_sleeping = FALSE;
11945
11946       player->dir_waiting = move_dir;
11947     }
11948
11949     if (player->is_sleeping)
11950     {
11951       if (player->num_special_action_sleeping > 0)
11952       {
11953         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11954         {
11955           int last_special_action = player->special_action_sleeping;
11956           int num_special_action = player->num_special_action_sleeping;
11957           int special_action =
11958             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11959              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11960              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11961              last_special_action + 1 : ACTION_SLEEPING);
11962           int special_graphic =
11963             el_act_dir2img(player->artwork_element, special_action, move_dir);
11964
11965           player->anim_delay_counter =
11966             graphic_info[special_graphic].anim_delay_fixed +
11967             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11968           player->post_delay_counter =
11969             graphic_info[special_graphic].post_delay_fixed +
11970             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11971
11972           player->special_action_sleeping = special_action;
11973         }
11974
11975         if (player->anim_delay_counter > 0)
11976         {
11977           player->action_waiting = player->special_action_sleeping;
11978           player->anim_delay_counter--;
11979         }
11980         else if (player->post_delay_counter > 0)
11981         {
11982           player->post_delay_counter--;
11983         }
11984       }
11985     }
11986     else if (player->is_bored)
11987     {
11988       if (player->num_special_action_bored > 0)
11989       {
11990         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11991         {
11992           int special_action =
11993             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11994           int special_graphic =
11995             el_act_dir2img(player->artwork_element, special_action, move_dir);
11996
11997           player->anim_delay_counter =
11998             graphic_info[special_graphic].anim_delay_fixed +
11999             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12000           player->post_delay_counter =
12001             graphic_info[special_graphic].post_delay_fixed +
12002             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12003
12004           player->special_action_bored = special_action;
12005         }
12006
12007         if (player->anim_delay_counter > 0)
12008         {
12009           player->action_waiting = player->special_action_bored;
12010           player->anim_delay_counter--;
12011         }
12012         else if (player->post_delay_counter > 0)
12013         {
12014           player->post_delay_counter--;
12015         }
12016       }
12017     }
12018   }
12019   else if (last_waiting)        /* waiting -> not waiting */
12020   {
12021     player->is_waiting = FALSE;
12022     player->is_bored = FALSE;
12023     player->is_sleeping = FALSE;
12024
12025     player->frame_counter_bored = -1;
12026     player->frame_counter_sleeping = -1;
12027
12028     player->anim_delay_counter = 0;
12029     player->post_delay_counter = 0;
12030
12031     player->dir_waiting = player->MovDir;
12032     player->action_waiting = ACTION_DEFAULT;
12033
12034     player->special_action_bored = ACTION_DEFAULT;
12035     player->special_action_sleeping = ACTION_DEFAULT;
12036   }
12037 }
12038
12039 static void CheckSingleStepMode(struct PlayerInfo *player)
12040 {
12041   if (tape.single_step && tape.recording && !tape.pausing)
12042   {
12043     /* as it is called "single step mode", just return to pause mode when the
12044        player stopped moving after one tile (or never starts moving at all) */
12045     if (!player->is_moving && !player->is_pushing)
12046     {
12047       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12048       SnapField(player, 0, 0);                  /* stop snapping */
12049     }
12050   }
12051 }
12052
12053 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12054 {
12055   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12056   int left      = player_action & JOY_LEFT;
12057   int right     = player_action & JOY_RIGHT;
12058   int up        = player_action & JOY_UP;
12059   int down      = player_action & JOY_DOWN;
12060   int button1   = player_action & JOY_BUTTON_1;
12061   int button2   = player_action & JOY_BUTTON_2;
12062   int dx        = (left ? -1 : right ? 1 : 0);
12063   int dy        = (up   ? -1 : down  ? 1 : 0);
12064
12065   if (!player->active || tape.pausing)
12066     return 0;
12067
12068   if (player_action)
12069   {
12070     if (button1)
12071       snapped = SnapField(player, dx, dy);
12072     else
12073     {
12074       if (button2)
12075         dropped = DropElement(player);
12076
12077       moved = MovePlayer(player, dx, dy);
12078     }
12079
12080     CheckSingleStepMode(player);
12081
12082     SetPlayerWaiting(player, FALSE);
12083
12084     return player_action;
12085   }
12086   else
12087   {
12088     /* no actions for this player (no input at player's configured device) */
12089
12090     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12091     SnapField(player, 0, 0);
12092     CheckGravityMovementWhenNotMoving(player);
12093
12094     if (player->MovPos == 0)
12095       SetPlayerWaiting(player, TRUE);
12096
12097     if (player->MovPos == 0)    /* needed for tape.playing */
12098       player->is_moving = FALSE;
12099
12100     player->is_dropping = FALSE;
12101     player->is_dropping_pressed = FALSE;
12102     player->drop_pressed_delay = 0;
12103
12104     CheckSingleStepMode(player);
12105
12106     return 0;
12107   }
12108 }
12109
12110 static void CheckLevelTime()
12111 {
12112   int i;
12113
12114   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12115   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12116   {
12117     if (level.native_em_level->lev->home == 0)  /* all players at home */
12118     {
12119       PlayerWins(local_player);
12120
12121       AllPlayersGone = TRUE;
12122
12123       level.native_em_level->lev->home = -1;
12124     }
12125
12126     if (level.native_em_level->ply[0]->alive == 0 &&
12127         level.native_em_level->ply[1]->alive == 0 &&
12128         level.native_em_level->ply[2]->alive == 0 &&
12129         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12130       AllPlayersGone = TRUE;
12131   }
12132   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12133   {
12134     if (game_sp.LevelSolved &&
12135         !game_sp.GameOver)                              /* game won */
12136     {
12137       PlayerWins(local_player);
12138
12139       game_sp.GameOver = TRUE;
12140
12141       AllPlayersGone = TRUE;
12142     }
12143
12144     if (game_sp.GameOver)                               /* game lost */
12145       AllPlayersGone = TRUE;
12146   }
12147
12148   if (TimeFrames >= FRAMES_PER_SECOND)
12149   {
12150     TimeFrames = 0;
12151     TapeTime++;
12152
12153     for (i = 0; i < MAX_PLAYERS; i++)
12154     {
12155       struct PlayerInfo *player = &stored_player[i];
12156
12157       if (SHIELD_ON(player))
12158       {
12159         player->shield_normal_time_left--;
12160
12161         if (player->shield_deadly_time_left > 0)
12162           player->shield_deadly_time_left--;
12163       }
12164     }
12165
12166     if (!local_player->LevelSolved && !level.use_step_counter)
12167     {
12168       TimePlayed++;
12169
12170       if (TimeLeft > 0)
12171       {
12172         TimeLeft--;
12173
12174         if (TimeLeft <= 10 && setup.time_limit)
12175           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12176
12177 #if 1
12178         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12179
12180         DisplayGameControlValues();
12181 #else
12182         DrawGameValue_Time(TimeLeft);
12183 #endif
12184
12185         if (!TimeLeft && setup.time_limit)
12186         {
12187           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12188             level.native_em_level->lev->killed_out_of_time = TRUE;
12189           else
12190             for (i = 0; i < MAX_PLAYERS; i++)
12191               KillPlayer(&stored_player[i]);
12192         }
12193       }
12194 #if 1
12195       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12196       {
12197         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12198
12199         DisplayGameControlValues();
12200       }
12201 #else
12202       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12203         DrawGameValue_Time(TimePlayed);
12204 #endif
12205
12206       level.native_em_level->lev->time =
12207         (level.time == 0 ? TimePlayed : TimeLeft);
12208     }
12209
12210     if (tape.recording || tape.playing)
12211       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12212   }
12213
12214 #if 1
12215   UpdateAndDisplayGameControlValues();
12216 #else
12217   UpdateGameDoorValues();
12218   DrawGameDoorValues();
12219 #endif
12220 }
12221
12222 void AdvanceFrameAndPlayerCounters(int player_nr)
12223 {
12224   int i;
12225
12226   /* advance frame counters (global frame counter and time frame counter) */
12227   FrameCounter++;
12228   TimeFrames++;
12229
12230   /* advance player counters (counters for move delay, move animation etc.) */
12231   for (i = 0; i < MAX_PLAYERS; i++)
12232   {
12233     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12234     int move_delay_value = stored_player[i].move_delay_value;
12235     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12236
12237     if (!advance_player_counters)       /* not all players may be affected */
12238       continue;
12239
12240 #if USE_NEW_PLAYER_ANIM
12241     if (move_frames == 0)       /* less than one move per game frame */
12242     {
12243       int stepsize = TILEX / move_delay_value;
12244       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12245       int count = (stored_player[i].is_moving ?
12246                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12247
12248       if (count % delay == 0)
12249         move_frames = 1;
12250     }
12251 #endif
12252
12253     stored_player[i].Frame += move_frames;
12254
12255     if (stored_player[i].MovPos != 0)
12256       stored_player[i].StepFrame += move_frames;
12257
12258     if (stored_player[i].move_delay > 0)
12259       stored_player[i].move_delay--;
12260
12261     /* due to bugs in previous versions, counter must count up, not down */
12262     if (stored_player[i].push_delay != -1)
12263       stored_player[i].push_delay++;
12264
12265     if (stored_player[i].drop_delay > 0)
12266       stored_player[i].drop_delay--;
12267
12268     if (stored_player[i].is_dropping_pressed)
12269       stored_player[i].drop_pressed_delay++;
12270   }
12271 }
12272
12273 void StartGameActions(boolean init_network_game, boolean record_tape,
12274                       long random_seed)
12275 {
12276   unsigned long new_random_seed = InitRND(random_seed);
12277
12278   if (record_tape)
12279     TapeStartRecording(new_random_seed);
12280
12281 #if defined(NETWORK_AVALIABLE)
12282   if (init_network_game)
12283   {
12284     SendToServer_StartPlaying();
12285
12286     return;
12287   }
12288 #endif
12289
12290   InitGame();
12291 }
12292
12293 void GameActions()
12294 {
12295   static unsigned long game_frame_delay = 0;
12296   unsigned long game_frame_delay_value;
12297   byte *recorded_player_action;
12298   byte summarized_player_action = 0;
12299   byte tape_action[MAX_PLAYERS];
12300   int i;
12301
12302   /* detect endless loops, caused by custom element programming */
12303   if (recursion_loop_detected && recursion_loop_depth == 0)
12304   {
12305     char *message = getStringCat3("Internal Error ! Element ",
12306                                   EL_NAME(recursion_loop_element),
12307                                   " caused endless loop ! Quit the game ?");
12308
12309     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12310           EL_NAME(recursion_loop_element));
12311
12312     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12313
12314     recursion_loop_detected = FALSE;    /* if game should be continued */
12315
12316     free(message);
12317
12318     return;
12319   }
12320
12321   if (game.restart_level)
12322     StartGameActions(options.network, setup.autorecord, level.random_seed);
12323
12324   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12325   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12326   {
12327     if (level.native_em_level->lev->home == 0)  /* all players at home */
12328     {
12329       PlayerWins(local_player);
12330
12331       AllPlayersGone = TRUE;
12332
12333       level.native_em_level->lev->home = -1;
12334     }
12335
12336     if (level.native_em_level->ply[0]->alive == 0 &&
12337         level.native_em_level->ply[1]->alive == 0 &&
12338         level.native_em_level->ply[2]->alive == 0 &&
12339         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12340       AllPlayersGone = TRUE;
12341   }
12342   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12343   {
12344     if (game_sp.LevelSolved &&
12345         !game_sp.GameOver)                              /* game won */
12346     {
12347       PlayerWins(local_player);
12348
12349       game_sp.GameOver = TRUE;
12350
12351       AllPlayersGone = TRUE;
12352     }
12353
12354     if (game_sp.GameOver)                               /* game lost */
12355       AllPlayersGone = TRUE;
12356   }
12357
12358   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12359     GameWon();
12360
12361   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12362     TapeStop();
12363
12364   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12365     return;
12366
12367   game_frame_delay_value =
12368     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12369
12370   if (tape.playing && tape.warp_forward && !tape.pausing)
12371     game_frame_delay_value = 0;
12372
12373   /* ---------- main game synchronization point ---------- */
12374
12375   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12376
12377   if (network_playing && !network_player_action_received)
12378   {
12379     /* try to get network player actions in time */
12380
12381 #if defined(NETWORK_AVALIABLE)
12382     /* last chance to get network player actions without main loop delay */
12383     HandleNetworking();
12384 #endif
12385
12386     /* game was quit by network peer */
12387     if (game_status != GAME_MODE_PLAYING)
12388       return;
12389
12390     if (!network_player_action_received)
12391       return;           /* failed to get network player actions in time */
12392
12393     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12394   }
12395
12396   if (tape.pausing)
12397     return;
12398
12399   /* at this point we know that we really continue executing the game */
12400
12401   network_player_action_received = FALSE;
12402
12403   /* when playing tape, read previously recorded player input from tape data */
12404   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12405
12406 #if 1
12407   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12408   if (tape.pausing)
12409     return;
12410 #endif
12411
12412   if (tape.set_centered_player)
12413   {
12414     game.centered_player_nr_next = tape.centered_player_nr_next;
12415     game.set_centered_player = TRUE;
12416   }
12417
12418   for (i = 0; i < MAX_PLAYERS; i++)
12419   {
12420     summarized_player_action |= stored_player[i].action;
12421
12422     if (!network_playing)
12423       stored_player[i].effective_action = stored_player[i].action;
12424   }
12425
12426 #if defined(NETWORK_AVALIABLE)
12427   if (network_playing)
12428     SendToServer_MovePlayer(summarized_player_action);
12429 #endif
12430
12431   if (!options.network && !setup.team_mode)
12432     local_player->effective_action = summarized_player_action;
12433
12434   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12435   {
12436     for (i = 0; i < MAX_PLAYERS; i++)
12437       stored_player[i].effective_action =
12438         (i == game.centered_player_nr ? summarized_player_action : 0);
12439   }
12440
12441   if (recorded_player_action != NULL)
12442     for (i = 0; i < MAX_PLAYERS; i++)
12443       stored_player[i].effective_action = recorded_player_action[i];
12444
12445   for (i = 0; i < MAX_PLAYERS; i++)
12446   {
12447     tape_action[i] = stored_player[i].effective_action;
12448
12449     /* (this can only happen in the R'n'D game engine) */
12450     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12451       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12452   }
12453
12454   /* only record actions from input devices, but not programmed actions */
12455   if (tape.recording)
12456     TapeRecordAction(tape_action);
12457
12458 #if USE_NEW_PLAYER_ASSIGNMENTS
12459   {
12460     byte mapped_action[MAX_PLAYERS];
12461
12462     for (i = 0; i < MAX_PLAYERS; i++)
12463       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12464
12465     for (i = 0; i < MAX_PLAYERS; i++)
12466       stored_player[i].effective_action = mapped_action[i];
12467   }
12468 #endif
12469
12470   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12471   {
12472     GameActions_EM_Main();
12473   }
12474   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12475   {
12476     GameActions_SP_Main();
12477   }
12478   else
12479   {
12480     GameActions_RND();
12481   }
12482 }
12483
12484 void GameActions_EM_Main()
12485 {
12486   byte effective_action[MAX_PLAYERS];
12487   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12488   int i;
12489
12490   for (i = 0; i < MAX_PLAYERS; i++)
12491     effective_action[i] = stored_player[i].effective_action;
12492
12493   GameActions_EM(effective_action, warp_mode);
12494
12495   CheckLevelTime();
12496
12497   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12498 }
12499
12500 void GameActions_SP_Main()
12501 {
12502   byte effective_action[MAX_PLAYERS];
12503   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12504   int i;
12505
12506   for (i = 0; i < MAX_PLAYERS; i++)
12507     effective_action[i] = stored_player[i].effective_action;
12508
12509   GameActions_SP(effective_action, warp_mode);
12510
12511   CheckLevelTime();
12512
12513   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12514 }
12515
12516 void GameActions_RND()
12517 {
12518   int magic_wall_x = 0, magic_wall_y = 0;
12519   int i, x, y, element, graphic;
12520
12521   InitPlayfieldScanModeVars();
12522
12523 #if USE_ONE_MORE_CHANGE_PER_FRAME
12524   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12525   {
12526     SCAN_PLAYFIELD(x, y)
12527     {
12528       ChangeCount[x][y] = 0;
12529       ChangeEvent[x][y] = -1;
12530     }
12531   }
12532 #endif
12533
12534   if (game.set_centered_player)
12535   {
12536     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12537
12538     /* switching to "all players" only possible if all players fit to screen */
12539     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12540     {
12541       game.centered_player_nr_next = game.centered_player_nr;
12542       game.set_centered_player = FALSE;
12543     }
12544
12545     /* do not switch focus to non-existing (or non-active) player */
12546     if (game.centered_player_nr_next >= 0 &&
12547         !stored_player[game.centered_player_nr_next].active)
12548     {
12549       game.centered_player_nr_next = game.centered_player_nr;
12550       game.set_centered_player = FALSE;
12551     }
12552   }
12553
12554   if (game.set_centered_player &&
12555       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12556   {
12557     int sx, sy;
12558
12559     if (game.centered_player_nr_next == -1)
12560     {
12561       setScreenCenteredToAllPlayers(&sx, &sy);
12562     }
12563     else
12564     {
12565       sx = stored_player[game.centered_player_nr_next].jx;
12566       sy = stored_player[game.centered_player_nr_next].jy;
12567     }
12568
12569     game.centered_player_nr = game.centered_player_nr_next;
12570     game.set_centered_player = FALSE;
12571
12572     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12573     DrawGameDoorValues();
12574   }
12575
12576   for (i = 0; i < MAX_PLAYERS; i++)
12577   {
12578     int actual_player_action = stored_player[i].effective_action;
12579
12580 #if 1
12581     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12582        - rnd_equinox_tetrachloride 048
12583        - rnd_equinox_tetrachloride_ii 096
12584        - rnd_emanuel_schmieg 002
12585        - doctor_sloan_ww 001, 020
12586     */
12587     if (stored_player[i].MovPos == 0)
12588       CheckGravityMovement(&stored_player[i]);
12589 #endif
12590
12591     /* overwrite programmed action with tape action */
12592     if (stored_player[i].programmed_action)
12593       actual_player_action = stored_player[i].programmed_action;
12594
12595     PlayerActions(&stored_player[i], actual_player_action);
12596
12597     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12598   }
12599
12600   ScrollScreen(NULL, SCROLL_GO_ON);
12601
12602   /* for backwards compatibility, the following code emulates a fixed bug that
12603      occured when pushing elements (causing elements that just made their last
12604      pushing step to already (if possible) make their first falling step in the
12605      same game frame, which is bad); this code is also needed to use the famous
12606      "spring push bug" which is used in older levels and might be wanted to be
12607      used also in newer levels, but in this case the buggy pushing code is only
12608      affecting the "spring" element and no other elements */
12609
12610   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12611   {
12612     for (i = 0; i < MAX_PLAYERS; i++)
12613     {
12614       struct PlayerInfo *player = &stored_player[i];
12615       int x = player->jx;
12616       int y = player->jy;
12617
12618       if (player->active && player->is_pushing && player->is_moving &&
12619           IS_MOVING(x, y) &&
12620           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12621            Feld[x][y] == EL_SPRING))
12622       {
12623         ContinueMoving(x, y);
12624
12625         /* continue moving after pushing (this is actually a bug) */
12626         if (!IS_MOVING(x, y))
12627           Stop[x][y] = FALSE;
12628       }
12629     }
12630   }
12631
12632 #if 0
12633   debug_print_timestamp(0, "start main loop profiling");
12634 #endif
12635
12636   SCAN_PLAYFIELD(x, y)
12637   {
12638     ChangeCount[x][y] = 0;
12639     ChangeEvent[x][y] = -1;
12640
12641     /* this must be handled before main playfield loop */
12642     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12643     {
12644       MovDelay[x][y]--;
12645       if (MovDelay[x][y] <= 0)
12646         RemoveField(x, y);
12647     }
12648
12649 #if USE_NEW_SNAP_DELAY
12650     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12651     {
12652       MovDelay[x][y]--;
12653       if (MovDelay[x][y] <= 0)
12654       {
12655         RemoveField(x, y);
12656         TEST_DrawLevelField(x, y);
12657
12658         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12659       }
12660     }
12661 #endif
12662
12663 #if DEBUG
12664     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12665     {
12666       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12667       printf("GameActions(): This should never happen!\n");
12668
12669       ChangePage[x][y] = -1;
12670     }
12671 #endif
12672
12673     Stop[x][y] = FALSE;
12674     if (WasJustMoving[x][y] > 0)
12675       WasJustMoving[x][y]--;
12676     if (WasJustFalling[x][y] > 0)
12677       WasJustFalling[x][y]--;
12678     if (CheckCollision[x][y] > 0)
12679       CheckCollision[x][y]--;
12680     if (CheckImpact[x][y] > 0)
12681       CheckImpact[x][y]--;
12682
12683     GfxFrame[x][y]++;
12684
12685     /* reset finished pushing action (not done in ContinueMoving() to allow
12686        continuous pushing animation for elements with zero push delay) */
12687     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12688     {
12689       ResetGfxAnimation(x, y);
12690       TEST_DrawLevelField(x, y);
12691     }
12692
12693 #if DEBUG
12694     if (IS_BLOCKED(x, y))
12695     {
12696       int oldx, oldy;
12697
12698       Blocked2Moving(x, y, &oldx, &oldy);
12699       if (!IS_MOVING(oldx, oldy))
12700       {
12701         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12702         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12703         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12704         printf("GameActions(): This should never happen!\n");
12705       }
12706     }
12707 #endif
12708   }
12709
12710 #if 0
12711   debug_print_timestamp(0, "- time for pre-main loop:");
12712 #endif
12713
12714 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12715   SCAN_PLAYFIELD(x, y)
12716   {
12717     element = Feld[x][y];
12718     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12719
12720 #if 1
12721     {
12722 #if 1
12723       int element2 = element;
12724       int graphic2 = graphic;
12725 #else
12726       int element2 = Feld[x][y];
12727       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12728 #endif
12729       int last_gfx_frame = GfxFrame[x][y];
12730
12731       if (graphic_info[graphic2].anim_global_sync)
12732         GfxFrame[x][y] = FrameCounter;
12733       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12734         GfxFrame[x][y] = CustomValue[x][y];
12735       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12736         GfxFrame[x][y] = element_info[element2].collect_score;
12737       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12738         GfxFrame[x][y] = ChangeDelay[x][y];
12739
12740       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12741         DrawLevelGraphicAnimation(x, y, graphic2);
12742     }
12743 #else
12744     ResetGfxFrame(x, y, TRUE);
12745 #endif
12746
12747 #if 1
12748     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12749         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12750       ResetRandomAnimationValue(x, y);
12751 #endif
12752
12753 #if 1
12754     SetRandomAnimationValue(x, y);
12755 #endif
12756
12757 #if 1
12758     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12759 #endif
12760   }
12761 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12762
12763 #if 0
12764   debug_print_timestamp(0, "- time for TEST loop:     -->");
12765 #endif
12766
12767   SCAN_PLAYFIELD(x, y)
12768   {
12769     element = Feld[x][y];
12770     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12771
12772     ResetGfxFrame(x, y, TRUE);
12773
12774     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12775         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12776       ResetRandomAnimationValue(x, y);
12777
12778     SetRandomAnimationValue(x, y);
12779
12780     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12781
12782     if (IS_INACTIVE(element))
12783     {
12784       if (IS_ANIMATED(graphic))
12785         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12786
12787       continue;
12788     }
12789
12790     /* this may take place after moving, so 'element' may have changed */
12791     if (IS_CHANGING(x, y) &&
12792         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12793     {
12794       int page = element_info[element].event_page_nr[CE_DELAY];
12795
12796 #if 1
12797       HandleElementChange(x, y, page);
12798 #else
12799       if (CAN_CHANGE(element))
12800         HandleElementChange(x, y, page);
12801
12802       if (HAS_ACTION(element))
12803         ExecuteCustomElementAction(x, y, element, page);
12804 #endif
12805
12806       element = Feld[x][y];
12807       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12808     }
12809
12810 #if 0   // ---------------------------------------------------------------------
12811
12812     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12813     {
12814       StartMoving(x, y);
12815
12816       element = Feld[x][y];
12817       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12818
12819       if (IS_ANIMATED(graphic) &&
12820           !IS_MOVING(x, y) &&
12821           !Stop[x][y])
12822         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12823
12824       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12825         TEST_DrawTwinkleOnField(x, y);
12826     }
12827     else if (IS_MOVING(x, y))
12828       ContinueMoving(x, y);
12829     else
12830     {
12831       switch (element)
12832       {
12833         case EL_ACID:
12834         case EL_EXIT_OPEN:
12835         case EL_EM_EXIT_OPEN:
12836         case EL_SP_EXIT_OPEN:
12837         case EL_STEEL_EXIT_OPEN:
12838         case EL_EM_STEEL_EXIT_OPEN:
12839         case EL_SP_TERMINAL:
12840         case EL_SP_TERMINAL_ACTIVE:
12841         case EL_EXTRA_TIME:
12842         case EL_SHIELD_NORMAL:
12843         case EL_SHIELD_DEADLY:
12844           if (IS_ANIMATED(graphic))
12845             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12846           break;
12847
12848         case EL_DYNAMITE_ACTIVE:
12849         case EL_EM_DYNAMITE_ACTIVE:
12850         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12851         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12852         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12853         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12854         case EL_SP_DISK_RED_ACTIVE:
12855           CheckDynamite(x, y);
12856           break;
12857
12858         case EL_AMOEBA_GROWING:
12859           AmoebeWaechst(x, y);
12860           break;
12861
12862         case EL_AMOEBA_SHRINKING:
12863           AmoebaDisappearing(x, y);
12864           break;
12865
12866 #if !USE_NEW_AMOEBA_CODE
12867         case EL_AMOEBA_WET:
12868         case EL_AMOEBA_DRY:
12869         case EL_AMOEBA_FULL:
12870         case EL_BD_AMOEBA:
12871         case EL_EMC_DRIPPER:
12872           AmoebeAbleger(x, y);
12873           break;
12874 #endif
12875
12876         case EL_GAME_OF_LIFE:
12877         case EL_BIOMAZE:
12878           Life(x, y);
12879           break;
12880
12881         case EL_EXIT_CLOSED:
12882           CheckExit(x, y);
12883           break;
12884
12885         case EL_EM_EXIT_CLOSED:
12886           CheckExitEM(x, y);
12887           break;
12888
12889         case EL_STEEL_EXIT_CLOSED:
12890           CheckExitSteel(x, y);
12891           break;
12892
12893         case EL_EM_STEEL_EXIT_CLOSED:
12894           CheckExitSteelEM(x, y);
12895           break;
12896
12897         case EL_SP_EXIT_CLOSED:
12898           CheckExitSP(x, y);
12899           break;
12900
12901         case EL_EXPANDABLE_WALL_GROWING:
12902         case EL_EXPANDABLE_STEELWALL_GROWING:
12903           MauerWaechst(x, y);
12904           break;
12905
12906         case EL_EXPANDABLE_WALL:
12907         case EL_EXPANDABLE_WALL_HORIZONTAL:
12908         case EL_EXPANDABLE_WALL_VERTICAL:
12909         case EL_EXPANDABLE_WALL_ANY:
12910         case EL_BD_EXPANDABLE_WALL:
12911           MauerAbleger(x, y);
12912           break;
12913
12914         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12915         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12916         case EL_EXPANDABLE_STEELWALL_ANY:
12917           MauerAblegerStahl(x, y);
12918           break;
12919
12920         case EL_FLAMES:
12921           CheckForDragon(x, y);
12922           break;
12923
12924         case EL_EXPLOSION:
12925           break;
12926
12927         case EL_ELEMENT_SNAPPING:
12928         case EL_DIAGONAL_SHRINKING:
12929         case EL_DIAGONAL_GROWING:
12930         {
12931           graphic =
12932             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12933
12934           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12935           break;
12936         }
12937
12938         default:
12939           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12940             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12941           break;
12942       }
12943     }
12944
12945 #else   // ---------------------------------------------------------------------
12946
12947     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12948     {
12949       StartMoving(x, y);
12950
12951       element = Feld[x][y];
12952       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12953
12954       if (IS_ANIMATED(graphic) &&
12955           !IS_MOVING(x, y) &&
12956           !Stop[x][y])
12957         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12958
12959       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12960         TEST_DrawTwinkleOnField(x, y);
12961     }
12962     else if ((element == EL_ACID ||
12963               element == EL_EXIT_OPEN ||
12964               element == EL_EM_EXIT_OPEN ||
12965               element == EL_SP_EXIT_OPEN ||
12966               element == EL_STEEL_EXIT_OPEN ||
12967               element == EL_EM_STEEL_EXIT_OPEN ||
12968               element == EL_SP_TERMINAL ||
12969               element == EL_SP_TERMINAL_ACTIVE ||
12970               element == EL_EXTRA_TIME ||
12971               element == EL_SHIELD_NORMAL ||
12972               element == EL_SHIELD_DEADLY) &&
12973              IS_ANIMATED(graphic))
12974       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12975     else if (IS_MOVING(x, y))
12976       ContinueMoving(x, y);
12977     else if (IS_ACTIVE_BOMB(element))
12978       CheckDynamite(x, y);
12979     else if (element == EL_AMOEBA_GROWING)
12980       AmoebeWaechst(x, y);
12981     else if (element == EL_AMOEBA_SHRINKING)
12982       AmoebaDisappearing(x, y);
12983
12984 #if !USE_NEW_AMOEBA_CODE
12985     else if (IS_AMOEBALIVE(element))
12986       AmoebeAbleger(x, y);
12987 #endif
12988
12989     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12990       Life(x, y);
12991     else if (element == EL_EXIT_CLOSED)
12992       CheckExit(x, y);
12993     else if (element == EL_EM_EXIT_CLOSED)
12994       CheckExitEM(x, y);
12995     else if (element == EL_STEEL_EXIT_CLOSED)
12996       CheckExitSteel(x, y);
12997     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12998       CheckExitSteelEM(x, y);
12999     else if (element == EL_SP_EXIT_CLOSED)
13000       CheckExitSP(x, y);
13001     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13002              element == EL_EXPANDABLE_STEELWALL_GROWING)
13003       MauerWaechst(x, y);
13004     else if (element == EL_EXPANDABLE_WALL ||
13005              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13006              element == EL_EXPANDABLE_WALL_VERTICAL ||
13007              element == EL_EXPANDABLE_WALL_ANY ||
13008              element == EL_BD_EXPANDABLE_WALL)
13009       MauerAbleger(x, y);
13010     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13011              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13012              element == EL_EXPANDABLE_STEELWALL_ANY)
13013       MauerAblegerStahl(x, y);
13014     else if (element == EL_FLAMES)
13015       CheckForDragon(x, y);
13016     else if (element == EL_EXPLOSION)
13017       ; /* drawing of correct explosion animation is handled separately */
13018     else if (element == EL_ELEMENT_SNAPPING ||
13019              element == EL_DIAGONAL_SHRINKING ||
13020              element == EL_DIAGONAL_GROWING)
13021     {
13022       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13023
13024       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13025     }
13026     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13027       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13028
13029 #endif  // ---------------------------------------------------------------------
13030
13031     if (IS_BELT_ACTIVE(element))
13032       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13033
13034     if (game.magic_wall_active)
13035     {
13036       int jx = local_player->jx, jy = local_player->jy;
13037
13038       /* play the element sound at the position nearest to the player */
13039       if ((element == EL_MAGIC_WALL_FULL ||
13040            element == EL_MAGIC_WALL_ACTIVE ||
13041            element == EL_MAGIC_WALL_EMPTYING ||
13042            element == EL_BD_MAGIC_WALL_FULL ||
13043            element == EL_BD_MAGIC_WALL_ACTIVE ||
13044            element == EL_BD_MAGIC_WALL_EMPTYING ||
13045            element == EL_DC_MAGIC_WALL_FULL ||
13046            element == EL_DC_MAGIC_WALL_ACTIVE ||
13047            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13048           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13049       {
13050         magic_wall_x = x;
13051         magic_wall_y = y;
13052       }
13053     }
13054   }
13055
13056 #if 0
13057   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13058 #endif
13059
13060 #if USE_NEW_AMOEBA_CODE
13061   /* new experimental amoeba growth stuff */
13062   if (!(FrameCounter % 8))
13063   {
13064     static unsigned long random = 1684108901;
13065
13066     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13067     {
13068       x = RND(lev_fieldx);
13069       y = RND(lev_fieldy);
13070       element = Feld[x][y];
13071
13072       if (!IS_PLAYER(x,y) &&
13073           (element == EL_EMPTY ||
13074            CAN_GROW_INTO(element) ||
13075            element == EL_QUICKSAND_EMPTY ||
13076            element == EL_QUICKSAND_FAST_EMPTY ||
13077            element == EL_ACID_SPLASH_LEFT ||
13078            element == EL_ACID_SPLASH_RIGHT))
13079       {
13080         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13081             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13082             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13083             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13084           Feld[x][y] = EL_AMOEBA_DROP;
13085       }
13086
13087       random = random * 129 + 1;
13088     }
13089   }
13090 #endif
13091
13092 #if 0
13093   if (game.explosions_delayed)
13094 #endif
13095   {
13096     game.explosions_delayed = FALSE;
13097
13098     SCAN_PLAYFIELD(x, y)
13099     {
13100       element = Feld[x][y];
13101
13102       if (ExplodeField[x][y])
13103         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13104       else if (element == EL_EXPLOSION)
13105         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13106
13107       ExplodeField[x][y] = EX_TYPE_NONE;
13108     }
13109
13110     game.explosions_delayed = TRUE;
13111   }
13112
13113   if (game.magic_wall_active)
13114   {
13115     if (!(game.magic_wall_time_left % 4))
13116     {
13117       int element = Feld[magic_wall_x][magic_wall_y];
13118
13119       if (element == EL_BD_MAGIC_WALL_FULL ||
13120           element == EL_BD_MAGIC_WALL_ACTIVE ||
13121           element == EL_BD_MAGIC_WALL_EMPTYING)
13122         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13123       else if (element == EL_DC_MAGIC_WALL_FULL ||
13124                element == EL_DC_MAGIC_WALL_ACTIVE ||
13125                element == EL_DC_MAGIC_WALL_EMPTYING)
13126         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13127       else
13128         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13129     }
13130
13131     if (game.magic_wall_time_left > 0)
13132     {
13133       game.magic_wall_time_left--;
13134
13135       if (!game.magic_wall_time_left)
13136       {
13137         SCAN_PLAYFIELD(x, y)
13138         {
13139           element = Feld[x][y];
13140
13141           if (element == EL_MAGIC_WALL_ACTIVE ||
13142               element == EL_MAGIC_WALL_FULL)
13143           {
13144             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13145             TEST_DrawLevelField(x, y);
13146           }
13147           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13148                    element == EL_BD_MAGIC_WALL_FULL)
13149           {
13150             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13151             TEST_DrawLevelField(x, y);
13152           }
13153           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13154                    element == EL_DC_MAGIC_WALL_FULL)
13155           {
13156             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13157             TEST_DrawLevelField(x, y);
13158           }
13159         }
13160
13161         game.magic_wall_active = FALSE;
13162       }
13163     }
13164   }
13165
13166   if (game.light_time_left > 0)
13167   {
13168     game.light_time_left--;
13169
13170     if (game.light_time_left == 0)
13171       RedrawAllLightSwitchesAndInvisibleElements();
13172   }
13173
13174   if (game.timegate_time_left > 0)
13175   {
13176     game.timegate_time_left--;
13177
13178     if (game.timegate_time_left == 0)
13179       CloseAllOpenTimegates();
13180   }
13181
13182   if (game.lenses_time_left > 0)
13183   {
13184     game.lenses_time_left--;
13185
13186     if (game.lenses_time_left == 0)
13187       RedrawAllInvisibleElementsForLenses();
13188   }
13189
13190   if (game.magnify_time_left > 0)
13191   {
13192     game.magnify_time_left--;
13193
13194     if (game.magnify_time_left == 0)
13195       RedrawAllInvisibleElementsForMagnifier();
13196   }
13197
13198   for (i = 0; i < MAX_PLAYERS; i++)
13199   {
13200     struct PlayerInfo *player = &stored_player[i];
13201
13202     if (SHIELD_ON(player))
13203     {
13204       if (player->shield_deadly_time_left)
13205         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13206       else if (player->shield_normal_time_left)
13207         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13208     }
13209   }
13210
13211 #if USE_DELAYED_GFX_REDRAW
13212   SCAN_PLAYFIELD(x, y)
13213   {
13214 #if 1
13215     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13216 #else
13217     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13218         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13219 #endif
13220     {
13221       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13222          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13223
13224       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13225         DrawLevelField(x, y);
13226
13227       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13228         DrawLevelFieldCrumbled(x, y);
13229
13230       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13231         DrawLevelFieldCrumbledNeighbours(x, y);
13232
13233       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13234         DrawTwinkleOnField(x, y);
13235     }
13236
13237     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13238   }
13239 #endif
13240
13241   CheckLevelTime();
13242
13243   DrawAllPlayers();
13244   PlayAllPlayersSound();
13245
13246   if (options.debug)                    /* calculate frames per second */
13247   {
13248     static unsigned long fps_counter = 0;
13249     static int fps_frames = 0;
13250     unsigned long fps_delay_ms = Counter() - fps_counter;
13251
13252     fps_frames++;
13253
13254     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13255     {
13256       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13257
13258       fps_frames = 0;
13259       fps_counter = Counter();
13260     }
13261
13262     redraw_mask |= REDRAW_FPS;
13263   }
13264
13265   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13266
13267   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13268   {
13269     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13270
13271     local_player->show_envelope = 0;
13272   }
13273
13274 #if 0
13275   debug_print_timestamp(0, "stop main loop profiling ");
13276   printf("----------------------------------------------------------\n");
13277 #endif
13278
13279   /* use random number generator in every frame to make it less predictable */
13280   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13281     RND(1);
13282 }
13283
13284 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13285 {
13286   int min_x = x, min_y = y, max_x = x, max_y = y;
13287   int i;
13288
13289   for (i = 0; i < MAX_PLAYERS; i++)
13290   {
13291     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13292
13293     if (!stored_player[i].active || &stored_player[i] == player)
13294       continue;
13295
13296     min_x = MIN(min_x, jx);
13297     min_y = MIN(min_y, jy);
13298     max_x = MAX(max_x, jx);
13299     max_y = MAX(max_y, jy);
13300   }
13301
13302   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13303 }
13304
13305 static boolean AllPlayersInVisibleScreen()
13306 {
13307   int i;
13308
13309   for (i = 0; i < MAX_PLAYERS; i++)
13310   {
13311     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13312
13313     if (!stored_player[i].active)
13314       continue;
13315
13316     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13317       return FALSE;
13318   }
13319
13320   return TRUE;
13321 }
13322
13323 void ScrollLevel(int dx, int dy)
13324 {
13325 #if 0
13326   /* (directly solved in BlitBitmap() now) */
13327   static Bitmap *bitmap_db_field2 = NULL;
13328   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13329   int x, y;
13330 #else
13331   int x, y;
13332 #endif
13333
13334 #if 0
13335   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13336   /* only horizontal XOR vertical scroll direction allowed */
13337   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13338     return;
13339 #endif
13340
13341 #if 0
13342   /* (directly solved in BlitBitmap() now) */
13343   if (bitmap_db_field2 == NULL)
13344     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13345
13346   /* needed when blitting directly to same bitmap -- should not be needed with
13347      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13348   BlitBitmap(drawto_field, bitmap_db_field2,
13349              FX + TILEX * (dx == -1) - softscroll_offset,
13350              FY + TILEY * (dy == -1) - softscroll_offset,
13351              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13352              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13353              FX + TILEX * (dx == 1) - softscroll_offset,
13354              FY + TILEY * (dy == 1) - softscroll_offset);
13355   BlitBitmap(bitmap_db_field2, drawto_field,
13356              FX + TILEX * (dx == 1) - softscroll_offset,
13357              FY + TILEY * (dy == 1) - softscroll_offset,
13358              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13359              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13360              FX + TILEX * (dx == 1) - softscroll_offset,
13361              FY + TILEY * (dy == 1) - softscroll_offset);
13362
13363 #else
13364
13365 #if 0
13366   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13367   int xsize = (BX2 - BX1 + 1);
13368   int ysize = (BY2 - BY1 + 1);
13369   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13370   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13371   int step  = (start < end ? +1 : -1);
13372
13373   for (i = start; i != end; i += step)
13374   {
13375     BlitBitmap(drawto_field, drawto_field,
13376                FX + TILEX * (dx != 0 ? i + step : 0),
13377                FY + TILEY * (dy != 0 ? i + step : 0),
13378                TILEX * (dx != 0 ? 1 : xsize),
13379                TILEY * (dy != 0 ? 1 : ysize),
13380                FX + TILEX * (dx != 0 ? i : 0),
13381                FY + TILEY * (dy != 0 ? i : 0));
13382   }
13383
13384 #else
13385
13386 #if NEW_TILESIZE
13387 #if NEW_SCROLL
13388   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13389 #else
13390   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13391 #endif
13392 #else
13393 #if NEW_SCROLL
13394   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13395 #else
13396   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13397 #endif
13398 #endif
13399
13400 #if NEW_TILESIZE
13401   BlitBitmap(drawto_field, drawto_field,
13402              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13403              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13404              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13405              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13406              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13407              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13408 #else
13409   BlitBitmap(drawto_field, drawto_field,
13410              FX + TILEX * (dx == -1) - softscroll_offset,
13411              FY + TILEY * (dy == -1) - softscroll_offset,
13412              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13413              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13414              FX + TILEX * (dx == 1) - softscroll_offset,
13415              FY + TILEY * (dy == 1) - softscroll_offset);
13416 #endif
13417
13418 #endif
13419 #endif
13420
13421   if (dx != 0)
13422   {
13423     x = (dx == 1 ? BX1 : BX2);
13424     for (y = BY1; y <= BY2; y++)
13425       DrawScreenField(x, y);
13426   }
13427
13428   if (dy != 0)
13429   {
13430     y = (dy == 1 ? BY1 : BY2);
13431     for (x = BX1; x <= BX2; x++)
13432       DrawScreenField(x, y);
13433   }
13434
13435   redraw_mask |= REDRAW_FIELD;
13436 }
13437
13438 static boolean canFallDown(struct PlayerInfo *player)
13439 {
13440   int jx = player->jx, jy = player->jy;
13441
13442   return (IN_LEV_FIELD(jx, jy + 1) &&
13443           (IS_FREE(jx, jy + 1) ||
13444            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13445           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13446           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13447 }
13448
13449 static boolean canPassField(int x, int y, int move_dir)
13450 {
13451   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13452   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13453   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13454   int nextx = x + dx;
13455   int nexty = y + dy;
13456   int element = Feld[x][y];
13457
13458   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13459           !CAN_MOVE(element) &&
13460           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13461           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13462           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13463 }
13464
13465 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13466 {
13467   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13468   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13469   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13470   int newx = x + dx;
13471   int newy = y + dy;
13472
13473   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13474           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13475           (IS_DIGGABLE(Feld[newx][newy]) ||
13476            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13477            canPassField(newx, newy, move_dir)));
13478 }
13479
13480 static void CheckGravityMovement(struct PlayerInfo *player)
13481 {
13482 #if USE_PLAYER_GRAVITY
13483   if (player->gravity && !player->programmed_action)
13484 #else
13485   if (game.gravity && !player->programmed_action)
13486 #endif
13487   {
13488     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13489     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13490     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13491     int jx = player->jx, jy = player->jy;
13492     boolean player_is_moving_to_valid_field =
13493       (!player_is_snapping &&
13494        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13495         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13496     boolean player_can_fall_down = canFallDown(player);
13497
13498     if (player_can_fall_down &&
13499         !player_is_moving_to_valid_field)
13500       player->programmed_action = MV_DOWN;
13501   }
13502 }
13503
13504 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13505 {
13506   return CheckGravityMovement(player);
13507
13508 #if USE_PLAYER_GRAVITY
13509   if (player->gravity && !player->programmed_action)
13510 #else
13511   if (game.gravity && !player->programmed_action)
13512 #endif
13513   {
13514     int jx = player->jx, jy = player->jy;
13515     boolean field_under_player_is_free =
13516       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13517     boolean player_is_standing_on_valid_field =
13518       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13519        (IS_WALKABLE(Feld[jx][jy]) &&
13520         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13521
13522     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13523       player->programmed_action = MV_DOWN;
13524   }
13525 }
13526
13527 /*
13528   MovePlayerOneStep()
13529   -----------------------------------------------------------------------------
13530   dx, dy:               direction (non-diagonal) to try to move the player to
13531   real_dx, real_dy:     direction as read from input device (can be diagonal)
13532 */
13533
13534 boolean MovePlayerOneStep(struct PlayerInfo *player,
13535                           int dx, int dy, int real_dx, int real_dy)
13536 {
13537   int jx = player->jx, jy = player->jy;
13538   int new_jx = jx + dx, new_jy = jy + dy;
13539 #if !USE_FIXED_DONT_RUN_INTO
13540   int element;
13541 #endif
13542   int can_move;
13543   boolean player_can_move = !player->cannot_move;
13544
13545   if (!player->active || (!dx && !dy))
13546     return MP_NO_ACTION;
13547
13548   player->MovDir = (dx < 0 ? MV_LEFT :
13549                     dx > 0 ? MV_RIGHT :
13550                     dy < 0 ? MV_UP :
13551                     dy > 0 ? MV_DOWN :  MV_NONE);
13552
13553   if (!IN_LEV_FIELD(new_jx, new_jy))
13554     return MP_NO_ACTION;
13555
13556   if (!player_can_move)
13557   {
13558     if (player->MovPos == 0)
13559     {
13560       player->is_moving = FALSE;
13561       player->is_digging = FALSE;
13562       player->is_collecting = FALSE;
13563       player->is_snapping = FALSE;
13564       player->is_pushing = FALSE;
13565     }
13566   }
13567
13568 #if 1
13569   if (!options.network && game.centered_player_nr == -1 &&
13570       !AllPlayersInSight(player, new_jx, new_jy))
13571     return MP_NO_ACTION;
13572 #else
13573   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13574     return MP_NO_ACTION;
13575 #endif
13576
13577 #if !USE_FIXED_DONT_RUN_INTO
13578   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13579
13580   /* (moved to DigField()) */
13581   if (player_can_move && DONT_RUN_INTO(element))
13582   {
13583     if (element == EL_ACID && dx == 0 && dy == 1)
13584     {
13585       SplashAcid(new_jx, new_jy);
13586       Feld[jx][jy] = EL_PLAYER_1;
13587       InitMovingField(jx, jy, MV_DOWN);
13588       Store[jx][jy] = EL_ACID;
13589       ContinueMoving(jx, jy);
13590       BuryPlayer(player);
13591     }
13592     else
13593       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13594
13595     return MP_MOVING;
13596   }
13597 #endif
13598
13599   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13600   if (can_move != MP_MOVING)
13601     return can_move;
13602
13603   /* check if DigField() has caused relocation of the player */
13604   if (player->jx != jx || player->jy != jy)
13605     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13606
13607   StorePlayer[jx][jy] = 0;
13608   player->last_jx = jx;
13609   player->last_jy = jy;
13610   player->jx = new_jx;
13611   player->jy = new_jy;
13612   StorePlayer[new_jx][new_jy] = player->element_nr;
13613
13614   if (player->move_delay_value_next != -1)
13615   {
13616     player->move_delay_value = player->move_delay_value_next;
13617     player->move_delay_value_next = -1;
13618   }
13619
13620   player->MovPos =
13621     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13622
13623   player->step_counter++;
13624
13625   PlayerVisit[jx][jy] = FrameCounter;
13626
13627 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13628   player->is_moving = TRUE;
13629 #endif
13630
13631 #if 1
13632   /* should better be called in MovePlayer(), but this breaks some tapes */
13633   ScrollPlayer(player, SCROLL_INIT);
13634 #endif
13635
13636   return MP_MOVING;
13637 }
13638
13639 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13640 {
13641   int jx = player->jx, jy = player->jy;
13642   int old_jx = jx, old_jy = jy;
13643   int moved = MP_NO_ACTION;
13644
13645   if (!player->active)
13646     return FALSE;
13647
13648   if (!dx && !dy)
13649   {
13650     if (player->MovPos == 0)
13651     {
13652       player->is_moving = FALSE;
13653       player->is_digging = FALSE;
13654       player->is_collecting = FALSE;
13655       player->is_snapping = FALSE;
13656       player->is_pushing = FALSE;
13657     }
13658
13659     return FALSE;
13660   }
13661
13662   if (player->move_delay > 0)
13663     return FALSE;
13664
13665   player->move_delay = -1;              /* set to "uninitialized" value */
13666
13667   /* store if player is automatically moved to next field */
13668   player->is_auto_moving = (player->programmed_action != MV_NONE);
13669
13670   /* remove the last programmed player action */
13671   player->programmed_action = 0;
13672
13673   if (player->MovPos)
13674   {
13675     /* should only happen if pre-1.2 tape recordings are played */
13676     /* this is only for backward compatibility */
13677
13678     int original_move_delay_value = player->move_delay_value;
13679
13680 #if DEBUG
13681     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13682            tape.counter);
13683 #endif
13684
13685     /* scroll remaining steps with finest movement resolution */
13686     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13687
13688     while (player->MovPos)
13689     {
13690       ScrollPlayer(player, SCROLL_GO_ON);
13691       ScrollScreen(NULL, SCROLL_GO_ON);
13692
13693       AdvanceFrameAndPlayerCounters(player->index_nr);
13694
13695       DrawAllPlayers();
13696       BackToFront();
13697     }
13698
13699     player->move_delay_value = original_move_delay_value;
13700   }
13701
13702   player->is_active = FALSE;
13703
13704   if (player->last_move_dir & MV_HORIZONTAL)
13705   {
13706     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13707       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13708   }
13709   else
13710   {
13711     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13712       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13713   }
13714
13715 #if USE_FIXED_BORDER_RUNNING_GFX
13716   if (!moved && !player->is_active)
13717   {
13718     player->is_moving = FALSE;
13719     player->is_digging = FALSE;
13720     player->is_collecting = FALSE;
13721     player->is_snapping = FALSE;
13722     player->is_pushing = FALSE;
13723   }
13724 #endif
13725
13726   jx = player->jx;
13727   jy = player->jy;
13728
13729 #if 1
13730   if (moved & MP_MOVING && !ScreenMovPos &&
13731       (player->index_nr == game.centered_player_nr ||
13732        game.centered_player_nr == -1))
13733 #else
13734   if (moved & MP_MOVING && !ScreenMovPos &&
13735       (player == local_player || !options.network))
13736 #endif
13737   {
13738     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13739     int offset = game.scroll_delay_value;
13740
13741     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13742     {
13743       /* actual player has left the screen -- scroll in that direction */
13744       if (jx != old_jx)         /* player has moved horizontally */
13745         scroll_x += (jx - old_jx);
13746       else                      /* player has moved vertically */
13747         scroll_y += (jy - old_jy);
13748     }
13749     else
13750     {
13751       if (jx != old_jx)         /* player has moved horizontally */
13752       {
13753         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13754             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13755           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13756
13757         /* don't scroll over playfield boundaries */
13758         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13759           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13760
13761         /* don't scroll more than one field at a time */
13762         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13763
13764         /* don't scroll against the player's moving direction */
13765         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13766             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13767           scroll_x = old_scroll_x;
13768       }
13769       else                      /* player has moved vertically */
13770       {
13771         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13772             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13773           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13774
13775         /* don't scroll over playfield boundaries */
13776         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13777           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13778
13779         /* don't scroll more than one field at a time */
13780         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13781
13782         /* don't scroll against the player's moving direction */
13783         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13784             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13785           scroll_y = old_scroll_y;
13786       }
13787     }
13788
13789     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13790     {
13791 #if 1
13792       if (!options.network && game.centered_player_nr == -1 &&
13793           !AllPlayersInVisibleScreen())
13794       {
13795         scroll_x = old_scroll_x;
13796         scroll_y = old_scroll_y;
13797       }
13798       else
13799 #else
13800       if (!options.network && !AllPlayersInVisibleScreen())
13801       {
13802         scroll_x = old_scroll_x;
13803         scroll_y = old_scroll_y;
13804       }
13805       else
13806 #endif
13807       {
13808         ScrollScreen(player, SCROLL_INIT);
13809         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13810       }
13811     }
13812   }
13813
13814   player->StepFrame = 0;
13815
13816   if (moved & MP_MOVING)
13817   {
13818     if (old_jx != jx && old_jy == jy)
13819       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13820     else if (old_jx == jx && old_jy != jy)
13821       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13822
13823     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13824
13825     player->last_move_dir = player->MovDir;
13826     player->is_moving = TRUE;
13827     player->is_snapping = FALSE;
13828     player->is_switching = FALSE;
13829     player->is_dropping = FALSE;
13830     player->is_dropping_pressed = FALSE;
13831     player->drop_pressed_delay = 0;
13832
13833 #if 0
13834     /* should better be called here than above, but this breaks some tapes */
13835     ScrollPlayer(player, SCROLL_INIT);
13836 #endif
13837   }
13838   else
13839   {
13840     CheckGravityMovementWhenNotMoving(player);
13841
13842     player->is_moving = FALSE;
13843
13844     /* at this point, the player is allowed to move, but cannot move right now
13845        (e.g. because of something blocking the way) -- ensure that the player
13846        is also allowed to move in the next frame (in old versions before 3.1.1,
13847        the player was forced to wait again for eight frames before next try) */
13848
13849     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13850       player->move_delay = 0;   /* allow direct movement in the next frame */
13851   }
13852
13853   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13854     player->move_delay = player->move_delay_value;
13855
13856   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13857   {
13858     TestIfPlayerTouchesBadThing(jx, jy);
13859     TestIfPlayerTouchesCustomElement(jx, jy);
13860   }
13861
13862   if (!player->active)
13863     RemovePlayer(player);
13864
13865   return moved;
13866 }
13867
13868 void ScrollPlayer(struct PlayerInfo *player, int mode)
13869 {
13870   int jx = player->jx, jy = player->jy;
13871   int last_jx = player->last_jx, last_jy = player->last_jy;
13872   int move_stepsize = TILEX / player->move_delay_value;
13873
13874 #if USE_NEW_PLAYER_SPEED
13875   if (!player->active)
13876     return;
13877
13878   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13879     return;
13880 #else
13881   if (!player->active || player->MovPos == 0)
13882     return;
13883 #endif
13884
13885   if (mode == SCROLL_INIT)
13886   {
13887     player->actual_frame_counter = FrameCounter;
13888     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13889
13890     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13891         Feld[last_jx][last_jy] == EL_EMPTY)
13892     {
13893       int last_field_block_delay = 0;   /* start with no blocking at all */
13894       int block_delay_adjustment = player->block_delay_adjustment;
13895
13896       /* if player blocks last field, add delay for exactly one move */
13897       if (player->block_last_field)
13898       {
13899         last_field_block_delay += player->move_delay_value;
13900
13901         /* when blocking enabled, prevent moving up despite gravity */
13902 #if USE_PLAYER_GRAVITY
13903         if (player->gravity && player->MovDir == MV_UP)
13904           block_delay_adjustment = -1;
13905 #else
13906         if (game.gravity && player->MovDir == MV_UP)
13907           block_delay_adjustment = -1;
13908 #endif
13909       }
13910
13911       /* add block delay adjustment (also possible when not blocking) */
13912       last_field_block_delay += block_delay_adjustment;
13913
13914       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13915       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13916     }
13917
13918 #if USE_NEW_PLAYER_SPEED
13919     if (player->MovPos != 0)    /* player has not yet reached destination */
13920       return;
13921 #else
13922     return;
13923 #endif
13924   }
13925   else if (!FrameReached(&player->actual_frame_counter, 1))
13926     return;
13927
13928 #if USE_NEW_PLAYER_SPEED
13929   if (player->MovPos != 0)
13930   {
13931     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13932     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13933
13934     /* before DrawPlayer() to draw correct player graphic for this case */
13935     if (player->MovPos == 0)
13936       CheckGravityMovement(player);
13937   }
13938 #else
13939   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13940   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13941
13942   /* before DrawPlayer() to draw correct player graphic for this case */
13943   if (player->MovPos == 0)
13944     CheckGravityMovement(player);
13945 #endif
13946
13947   if (player->MovPos == 0)      /* player reached destination field */
13948   {
13949     if (player->move_delay_reset_counter > 0)
13950     {
13951       player->move_delay_reset_counter--;
13952
13953       if (player->move_delay_reset_counter == 0)
13954       {
13955         /* continue with normal speed after quickly moving through gate */
13956         HALVE_PLAYER_SPEED(player);
13957
13958         /* be able to make the next move without delay */
13959         player->move_delay = 0;
13960       }
13961     }
13962
13963     player->last_jx = jx;
13964     player->last_jy = jy;
13965
13966     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13967         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13968 #if 1
13969         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13970 #endif
13971         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13972         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13973 #if 1
13974         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13975 #endif
13976         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13977         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13978     {
13979       DrawPlayer(player);       /* needed here only to cleanup last field */
13980       RemovePlayer(player);
13981
13982       if (local_player->friends_still_needed == 0 ||
13983           IS_SP_ELEMENT(Feld[jx][jy]))
13984         PlayerWins(player);
13985     }
13986
13987     /* this breaks one level: "machine", level 000 */
13988     {
13989       int move_direction = player->MovDir;
13990       int enter_side = MV_DIR_OPPOSITE(move_direction);
13991       int leave_side = move_direction;
13992       int old_jx = last_jx;
13993       int old_jy = last_jy;
13994       int old_element = Feld[old_jx][old_jy];
13995       int new_element = Feld[jx][jy];
13996
13997       if (IS_CUSTOM_ELEMENT(old_element))
13998         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13999                                    CE_LEFT_BY_PLAYER,
14000                                    player->index_bit, leave_side);
14001
14002       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14003                                           CE_PLAYER_LEAVES_X,
14004                                           player->index_bit, leave_side);
14005
14006       if (IS_CUSTOM_ELEMENT(new_element))
14007         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14008                                    player->index_bit, enter_side);
14009
14010       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14011                                           CE_PLAYER_ENTERS_X,
14012                                           player->index_bit, enter_side);
14013
14014 #if USE_FIX_CE_ACTION_WITH_PLAYER
14015       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14016                                         CE_MOVE_OF_X, move_direction);
14017 #else
14018       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14019                                         CE_MOVE_OF_X, move_direction);
14020 #endif
14021     }
14022
14023     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14024     {
14025       TestIfPlayerTouchesBadThing(jx, jy);
14026       TestIfPlayerTouchesCustomElement(jx, jy);
14027
14028       /* needed because pushed element has not yet reached its destination,
14029          so it would trigger a change event at its previous field location */
14030       if (!player->is_pushing)
14031         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14032
14033       if (!player->active)
14034         RemovePlayer(player);
14035     }
14036
14037     if (!local_player->LevelSolved && level.use_step_counter)
14038     {
14039       int i;
14040
14041       TimePlayed++;
14042
14043       if (TimeLeft > 0)
14044       {
14045         TimeLeft--;
14046
14047         if (TimeLeft <= 10 && setup.time_limit)
14048           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14049
14050 #if 1
14051         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14052
14053         DisplayGameControlValues();
14054 #else
14055         DrawGameValue_Time(TimeLeft);
14056 #endif
14057
14058         if (!TimeLeft && setup.time_limit)
14059           for (i = 0; i < MAX_PLAYERS; i++)
14060             KillPlayer(&stored_player[i]);
14061       }
14062 #if 1
14063       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14064       {
14065         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14066
14067         DisplayGameControlValues();
14068       }
14069 #else
14070       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14071         DrawGameValue_Time(TimePlayed);
14072 #endif
14073     }
14074
14075     if (tape.single_step && tape.recording && !tape.pausing &&
14076         !player->programmed_action)
14077       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14078   }
14079 }
14080
14081 void ScrollScreen(struct PlayerInfo *player, int mode)
14082 {
14083   static unsigned long screen_frame_counter = 0;
14084
14085   if (mode == SCROLL_INIT)
14086   {
14087     /* set scrolling step size according to actual player's moving speed */
14088     ScrollStepSize = TILEX / player->move_delay_value;
14089
14090     screen_frame_counter = FrameCounter;
14091     ScreenMovDir = player->MovDir;
14092     ScreenMovPos = player->MovPos;
14093     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14094     return;
14095   }
14096   else if (!FrameReached(&screen_frame_counter, 1))
14097     return;
14098
14099   if (ScreenMovPos)
14100   {
14101     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14102     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14103     redraw_mask |= REDRAW_FIELD;
14104   }
14105   else
14106     ScreenMovDir = MV_NONE;
14107 }
14108
14109 void TestIfPlayerTouchesCustomElement(int x, int y)
14110 {
14111   static int xy[4][2] =
14112   {
14113     { 0, -1 },
14114     { -1, 0 },
14115     { +1, 0 },
14116     { 0, +1 }
14117   };
14118   static int trigger_sides[4][2] =
14119   {
14120     /* center side       border side */
14121     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14122     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14123     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14124     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14125   };
14126   static int touch_dir[4] =
14127   {
14128     MV_LEFT | MV_RIGHT,
14129     MV_UP   | MV_DOWN,
14130     MV_UP   | MV_DOWN,
14131     MV_LEFT | MV_RIGHT
14132   };
14133   int center_element = Feld[x][y];      /* should always be non-moving! */
14134   int i;
14135
14136   for (i = 0; i < NUM_DIRECTIONS; i++)
14137   {
14138     int xx = x + xy[i][0];
14139     int yy = y + xy[i][1];
14140     int center_side = trigger_sides[i][0];
14141     int border_side = trigger_sides[i][1];
14142     int border_element;
14143
14144     if (!IN_LEV_FIELD(xx, yy))
14145       continue;
14146
14147     if (IS_PLAYER(x, y))                /* player found at center element */
14148     {
14149       struct PlayerInfo *player = PLAYERINFO(x, y);
14150
14151       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14152         border_element = Feld[xx][yy];          /* may be moving! */
14153       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14154         border_element = Feld[xx][yy];
14155       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14156         border_element = MovingOrBlocked2Element(xx, yy);
14157       else
14158         continue;               /* center and border element do not touch */
14159
14160       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14161                                  player->index_bit, border_side);
14162       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14163                                           CE_PLAYER_TOUCHES_X,
14164                                           player->index_bit, border_side);
14165
14166 #if USE_FIX_CE_ACTION_WITH_PLAYER
14167       {
14168         /* use player element that is initially defined in the level playfield,
14169            not the player element that corresponds to the runtime player number
14170            (example: a level that contains EL_PLAYER_3 as the only player would
14171            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14172         int player_element = PLAYERINFO(x, y)->initial_element;
14173
14174         CheckElementChangeBySide(xx, yy, border_element, player_element,
14175                                  CE_TOUCHING_X, border_side);
14176       }
14177 #endif
14178     }
14179     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14180     {
14181       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14182
14183       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14184       {
14185         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14186           continue;             /* center and border element do not touch */
14187       }
14188
14189       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14190                                  player->index_bit, center_side);
14191       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14192                                           CE_PLAYER_TOUCHES_X,
14193                                           player->index_bit, center_side);
14194
14195 #if USE_FIX_CE_ACTION_WITH_PLAYER
14196       {
14197         /* use player element that is initially defined in the level playfield,
14198            not the player element that corresponds to the runtime player number
14199            (example: a level that contains EL_PLAYER_3 as the only player would
14200            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14201         int player_element = PLAYERINFO(xx, yy)->initial_element;
14202
14203         CheckElementChangeBySide(x, y, center_element, player_element,
14204                                  CE_TOUCHING_X, center_side);
14205       }
14206 #endif
14207
14208       break;
14209     }
14210   }
14211 }
14212
14213 #if USE_ELEMENT_TOUCHING_BUGFIX
14214
14215 void TestIfElementTouchesCustomElement(int x, int y)
14216 {
14217   static int xy[4][2] =
14218   {
14219     { 0, -1 },
14220     { -1, 0 },
14221     { +1, 0 },
14222     { 0, +1 }
14223   };
14224   static int trigger_sides[4][2] =
14225   {
14226     /* center side      border side */
14227     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14228     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14229     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14230     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14231   };
14232   static int touch_dir[4] =
14233   {
14234     MV_LEFT | MV_RIGHT,
14235     MV_UP   | MV_DOWN,
14236     MV_UP   | MV_DOWN,
14237     MV_LEFT | MV_RIGHT
14238   };
14239   boolean change_center_element = FALSE;
14240   int center_element = Feld[x][y];      /* should always be non-moving! */
14241   int border_element_old[NUM_DIRECTIONS];
14242   int i;
14243
14244   for (i = 0; i < NUM_DIRECTIONS; i++)
14245   {
14246     int xx = x + xy[i][0];
14247     int yy = y + xy[i][1];
14248     int border_element;
14249
14250     border_element_old[i] = -1;
14251
14252     if (!IN_LEV_FIELD(xx, yy))
14253       continue;
14254
14255     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14256       border_element = Feld[xx][yy];    /* may be moving! */
14257     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14258       border_element = Feld[xx][yy];
14259     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14260       border_element = MovingOrBlocked2Element(xx, yy);
14261     else
14262       continue;                 /* center and border element do not touch */
14263
14264     border_element_old[i] = border_element;
14265   }
14266
14267   for (i = 0; i < NUM_DIRECTIONS; i++)
14268   {
14269     int xx = x + xy[i][0];
14270     int yy = y + xy[i][1];
14271     int center_side = trigger_sides[i][0];
14272     int border_element = border_element_old[i];
14273
14274     if (border_element == -1)
14275       continue;
14276
14277     /* check for change of border element */
14278     CheckElementChangeBySide(xx, yy, border_element, center_element,
14279                              CE_TOUCHING_X, center_side);
14280
14281     /* (center element cannot be player, so we dont have to check this here) */
14282   }
14283
14284   for (i = 0; i < NUM_DIRECTIONS; i++)
14285   {
14286     int xx = x + xy[i][0];
14287     int yy = y + xy[i][1];
14288     int border_side = trigger_sides[i][1];
14289     int border_element = border_element_old[i];
14290
14291     if (border_element == -1)
14292       continue;
14293
14294     /* check for change of center element (but change it only once) */
14295     if (!change_center_element)
14296       change_center_element =
14297         CheckElementChangeBySide(x, y, center_element, border_element,
14298                                  CE_TOUCHING_X, border_side);
14299
14300 #if USE_FIX_CE_ACTION_WITH_PLAYER
14301     if (IS_PLAYER(xx, yy))
14302     {
14303       /* use player element that is initially defined in the level playfield,
14304          not the player element that corresponds to the runtime player number
14305          (example: a level that contains EL_PLAYER_3 as the only player would
14306          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14307       int player_element = PLAYERINFO(xx, yy)->initial_element;
14308
14309       CheckElementChangeBySide(x, y, center_element, player_element,
14310                                CE_TOUCHING_X, border_side);
14311     }
14312 #endif
14313   }
14314 }
14315
14316 #else
14317
14318 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14319 {
14320   static int xy[4][2] =
14321   {
14322     { 0, -1 },
14323     { -1, 0 },
14324     { +1, 0 },
14325     { 0, +1 }
14326   };
14327   static int trigger_sides[4][2] =
14328   {
14329     /* center side      border side */
14330     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14331     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14332     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14333     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14334   };
14335   static int touch_dir[4] =
14336   {
14337     MV_LEFT | MV_RIGHT,
14338     MV_UP   | MV_DOWN,
14339     MV_UP   | MV_DOWN,
14340     MV_LEFT | MV_RIGHT
14341   };
14342   boolean change_center_element = FALSE;
14343   int center_element = Feld[x][y];      /* should always be non-moving! */
14344   int i;
14345
14346   for (i = 0; i < NUM_DIRECTIONS; i++)
14347   {
14348     int xx = x + xy[i][0];
14349     int yy = y + xy[i][1];
14350     int center_side = trigger_sides[i][0];
14351     int border_side = trigger_sides[i][1];
14352     int border_element;
14353
14354     if (!IN_LEV_FIELD(xx, yy))
14355       continue;
14356
14357     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14358       border_element = Feld[xx][yy];    /* may be moving! */
14359     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14360       border_element = Feld[xx][yy];
14361     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14362       border_element = MovingOrBlocked2Element(xx, yy);
14363     else
14364       continue;                 /* center and border element do not touch */
14365
14366     /* check for change of center element (but change it only once) */
14367     if (!change_center_element)
14368       change_center_element =
14369         CheckElementChangeBySide(x, y, center_element, border_element,
14370                                  CE_TOUCHING_X, border_side);
14371
14372     /* check for change of border element */
14373     CheckElementChangeBySide(xx, yy, border_element, center_element,
14374                              CE_TOUCHING_X, center_side);
14375   }
14376 }
14377
14378 #endif
14379
14380 void TestIfElementHitsCustomElement(int x, int y, int direction)
14381 {
14382   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14383   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14384   int hitx = x + dx, hity = y + dy;
14385   int hitting_element = Feld[x][y];
14386   int touched_element;
14387
14388   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14389     return;
14390
14391   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14392                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14393
14394   if (IN_LEV_FIELD(hitx, hity))
14395   {
14396     int opposite_direction = MV_DIR_OPPOSITE(direction);
14397     int hitting_side = direction;
14398     int touched_side = opposite_direction;
14399     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14400                           MovDir[hitx][hity] != direction ||
14401                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14402
14403     object_hit = TRUE;
14404
14405     if (object_hit)
14406     {
14407       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14408                                CE_HITTING_X, touched_side);
14409
14410       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14411                                CE_HIT_BY_X, hitting_side);
14412
14413       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14414                                CE_HIT_BY_SOMETHING, opposite_direction);
14415
14416 #if USE_FIX_CE_ACTION_WITH_PLAYER
14417       if (IS_PLAYER(hitx, hity))
14418       {
14419         /* use player element that is initially defined in the level playfield,
14420            not the player element that corresponds to the runtime player number
14421            (example: a level that contains EL_PLAYER_3 as the only player would
14422            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14423         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14424
14425         CheckElementChangeBySide(x, y, hitting_element, player_element,
14426                                  CE_HITTING_X, touched_side);
14427       }
14428 #endif
14429     }
14430   }
14431
14432   /* "hitting something" is also true when hitting the playfield border */
14433   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14434                            CE_HITTING_SOMETHING, direction);
14435 }
14436
14437 #if 0
14438 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14439 {
14440   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14441   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14442   int hitx = x + dx, hity = y + dy;
14443   int hitting_element = Feld[x][y];
14444   int touched_element;
14445 #if 0
14446   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14447                         !IS_FREE(hitx, hity) &&
14448                         (!IS_MOVING(hitx, hity) ||
14449                          MovDir[hitx][hity] != direction ||
14450                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14451 #endif
14452
14453   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14454     return;
14455
14456 #if 0
14457   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14458     return;
14459 #endif
14460
14461   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14462                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14463
14464   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14465                            EP_CAN_SMASH_EVERYTHING, direction);
14466
14467   if (IN_LEV_FIELD(hitx, hity))
14468   {
14469     int opposite_direction = MV_DIR_OPPOSITE(direction);
14470     int hitting_side = direction;
14471     int touched_side = opposite_direction;
14472 #if 0
14473     int touched_element = MovingOrBlocked2Element(hitx, hity);
14474 #endif
14475 #if 1
14476     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14477                           MovDir[hitx][hity] != direction ||
14478                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14479
14480     object_hit = TRUE;
14481 #endif
14482
14483     if (object_hit)
14484     {
14485       int i;
14486
14487       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14488                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14489
14490       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14491                                CE_OTHER_IS_SMASHING, touched_side);
14492
14493       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14494                                CE_OTHER_GETS_SMASHED, hitting_side);
14495     }
14496   }
14497 }
14498 #endif
14499
14500 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14501 {
14502   int i, kill_x = -1, kill_y = -1;
14503
14504   int bad_element = -1;
14505   static int test_xy[4][2] =
14506   {
14507     { 0, -1 },
14508     { -1, 0 },
14509     { +1, 0 },
14510     { 0, +1 }
14511   };
14512   static int test_dir[4] =
14513   {
14514     MV_UP,
14515     MV_LEFT,
14516     MV_RIGHT,
14517     MV_DOWN
14518   };
14519
14520   for (i = 0; i < NUM_DIRECTIONS; i++)
14521   {
14522     int test_x, test_y, test_move_dir, test_element;
14523
14524     test_x = good_x + test_xy[i][0];
14525     test_y = good_y + test_xy[i][1];
14526
14527     if (!IN_LEV_FIELD(test_x, test_y))
14528       continue;
14529
14530     test_move_dir =
14531       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14532
14533     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14534
14535     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14536        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14537     */
14538     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14539         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14540     {
14541       kill_x = test_x;
14542       kill_y = test_y;
14543       bad_element = test_element;
14544
14545       break;
14546     }
14547   }
14548
14549   if (kill_x != -1 || kill_y != -1)
14550   {
14551     if (IS_PLAYER(good_x, good_y))
14552     {
14553       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14554
14555       if (player->shield_deadly_time_left > 0 &&
14556           !IS_INDESTRUCTIBLE(bad_element))
14557         Bang(kill_x, kill_y);
14558       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14559         KillPlayer(player);
14560     }
14561     else
14562       Bang(good_x, good_y);
14563   }
14564 }
14565
14566 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14567 {
14568   int i, kill_x = -1, kill_y = -1;
14569   int bad_element = Feld[bad_x][bad_y];
14570   static int test_xy[4][2] =
14571   {
14572     { 0, -1 },
14573     { -1, 0 },
14574     { +1, 0 },
14575     { 0, +1 }
14576   };
14577   static int touch_dir[4] =
14578   {
14579     MV_LEFT | MV_RIGHT,
14580     MV_UP   | MV_DOWN,
14581     MV_UP   | MV_DOWN,
14582     MV_LEFT | MV_RIGHT
14583   };
14584   static int test_dir[4] =
14585   {
14586     MV_UP,
14587     MV_LEFT,
14588     MV_RIGHT,
14589     MV_DOWN
14590   };
14591
14592   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14593     return;
14594
14595   for (i = 0; i < NUM_DIRECTIONS; i++)
14596   {
14597     int test_x, test_y, test_move_dir, test_element;
14598
14599     test_x = bad_x + test_xy[i][0];
14600     test_y = bad_y + test_xy[i][1];
14601
14602     if (!IN_LEV_FIELD(test_x, test_y))
14603       continue;
14604
14605     test_move_dir =
14606       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14607
14608     test_element = Feld[test_x][test_y];
14609
14610     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14611        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14612     */
14613     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14614         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14615     {
14616       /* good thing is player or penguin that does not move away */
14617       if (IS_PLAYER(test_x, test_y))
14618       {
14619         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14620
14621         if (bad_element == EL_ROBOT && player->is_moving)
14622           continue;     /* robot does not kill player if he is moving */
14623
14624         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14625         {
14626           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14627             continue;           /* center and border element do not touch */
14628         }
14629
14630         kill_x = test_x;
14631         kill_y = test_y;
14632
14633         break;
14634       }
14635       else if (test_element == EL_PENGUIN)
14636       {
14637         kill_x = test_x;
14638         kill_y = test_y;
14639
14640         break;
14641       }
14642     }
14643   }
14644
14645   if (kill_x != -1 || kill_y != -1)
14646   {
14647     if (IS_PLAYER(kill_x, kill_y))
14648     {
14649       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14650
14651       if (player->shield_deadly_time_left > 0 &&
14652           !IS_INDESTRUCTIBLE(bad_element))
14653         Bang(bad_x, bad_y);
14654       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14655         KillPlayer(player);
14656     }
14657     else
14658       Bang(kill_x, kill_y);
14659   }
14660 }
14661
14662 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14663 {
14664   int bad_element = Feld[bad_x][bad_y];
14665   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14666   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14667   int test_x = bad_x + dx, test_y = bad_y + dy;
14668   int test_move_dir, test_element;
14669   int kill_x = -1, kill_y = -1;
14670
14671   if (!IN_LEV_FIELD(test_x, test_y))
14672     return;
14673
14674   test_move_dir =
14675     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14676
14677   test_element = Feld[test_x][test_y];
14678
14679   if (test_move_dir != bad_move_dir)
14680   {
14681     /* good thing can be player or penguin that does not move away */
14682     if (IS_PLAYER(test_x, test_y))
14683     {
14684       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14685
14686       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14687          player as being hit when he is moving towards the bad thing, because
14688          the "get hit by" condition would be lost after the player stops) */
14689       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14690         return;         /* player moves away from bad thing */
14691
14692       kill_x = test_x;
14693       kill_y = test_y;
14694     }
14695     else if (test_element == EL_PENGUIN)
14696     {
14697       kill_x = test_x;
14698       kill_y = test_y;
14699     }
14700   }
14701
14702   if (kill_x != -1 || kill_y != -1)
14703   {
14704     if (IS_PLAYER(kill_x, kill_y))
14705     {
14706       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14707
14708       if (player->shield_deadly_time_left > 0 &&
14709           !IS_INDESTRUCTIBLE(bad_element))
14710         Bang(bad_x, bad_y);
14711       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14712         KillPlayer(player);
14713     }
14714     else
14715       Bang(kill_x, kill_y);
14716   }
14717 }
14718
14719 void TestIfPlayerTouchesBadThing(int x, int y)
14720 {
14721   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14722 }
14723
14724 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14725 {
14726   TestIfGoodThingHitsBadThing(x, y, move_dir);
14727 }
14728
14729 void TestIfBadThingTouchesPlayer(int x, int y)
14730 {
14731   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14732 }
14733
14734 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14735 {
14736   TestIfBadThingHitsGoodThing(x, y, move_dir);
14737 }
14738
14739 void TestIfFriendTouchesBadThing(int x, int y)
14740 {
14741   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14742 }
14743
14744 void TestIfBadThingTouchesFriend(int x, int y)
14745 {
14746   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14747 }
14748
14749 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14750 {
14751   int i, kill_x = bad_x, kill_y = bad_y;
14752   static int xy[4][2] =
14753   {
14754     { 0, -1 },
14755     { -1, 0 },
14756     { +1, 0 },
14757     { 0, +1 }
14758   };
14759
14760   for (i = 0; i < NUM_DIRECTIONS; i++)
14761   {
14762     int x, y, element;
14763
14764     x = bad_x + xy[i][0];
14765     y = bad_y + xy[i][1];
14766     if (!IN_LEV_FIELD(x, y))
14767       continue;
14768
14769     element = Feld[x][y];
14770     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14771         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14772     {
14773       kill_x = x;
14774       kill_y = y;
14775       break;
14776     }
14777   }
14778
14779   if (kill_x != bad_x || kill_y != bad_y)
14780     Bang(bad_x, bad_y);
14781 }
14782
14783 void KillPlayer(struct PlayerInfo *player)
14784 {
14785   int jx = player->jx, jy = player->jy;
14786
14787   if (!player->active)
14788     return;
14789
14790 #if 0
14791   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14792          player->killed, player->active, player->reanimated);
14793 #endif
14794
14795   /* the following code was introduced to prevent an infinite loop when calling
14796      -> Bang()
14797      -> CheckTriggeredElementChangeExt()
14798      -> ExecuteCustomElementAction()
14799      -> KillPlayer()
14800      -> (infinitely repeating the above sequence of function calls)
14801      which occurs when killing the player while having a CE with the setting
14802      "kill player X when explosion of <player X>"; the solution using a new
14803      field "player->killed" was chosen for backwards compatibility, although
14804      clever use of the fields "player->active" etc. would probably also work */
14805 #if 1
14806   if (player->killed)
14807     return;
14808 #endif
14809
14810   player->killed = TRUE;
14811
14812   /* remove accessible field at the player's position */
14813   Feld[jx][jy] = EL_EMPTY;
14814
14815   /* deactivate shield (else Bang()/Explode() would not work right) */
14816   player->shield_normal_time_left = 0;
14817   player->shield_deadly_time_left = 0;
14818
14819 #if 0
14820   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14821          player->killed, player->active, player->reanimated);
14822 #endif
14823
14824   Bang(jx, jy);
14825
14826 #if 0
14827   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14828          player->killed, player->active, player->reanimated);
14829 #endif
14830
14831 #if USE_PLAYER_REANIMATION
14832 #if 1
14833   if (player->reanimated)       /* killed player may have been reanimated */
14834     player->killed = player->reanimated = FALSE;
14835   else
14836     BuryPlayer(player);
14837 #else
14838   if (player->killed)           /* player may have been reanimated */
14839     BuryPlayer(player);
14840 #endif
14841 #else
14842   BuryPlayer(player);
14843 #endif
14844 }
14845
14846 static void KillPlayerUnlessEnemyProtected(int x, int y)
14847 {
14848   if (!PLAYER_ENEMY_PROTECTED(x, y))
14849     KillPlayer(PLAYERINFO(x, y));
14850 }
14851
14852 static void KillPlayerUnlessExplosionProtected(int x, int y)
14853 {
14854   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14855     KillPlayer(PLAYERINFO(x, y));
14856 }
14857
14858 void BuryPlayer(struct PlayerInfo *player)
14859 {
14860   int jx = player->jx, jy = player->jy;
14861
14862   if (!player->active)
14863     return;
14864
14865   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14866   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14867
14868   player->GameOver = TRUE;
14869   RemovePlayer(player);
14870 }
14871
14872 void RemovePlayer(struct PlayerInfo *player)
14873 {
14874   int jx = player->jx, jy = player->jy;
14875   int i, found = FALSE;
14876
14877   player->present = FALSE;
14878   player->active = FALSE;
14879
14880   if (!ExplodeField[jx][jy])
14881     StorePlayer[jx][jy] = 0;
14882
14883   if (player->is_moving)
14884     TEST_DrawLevelField(player->last_jx, player->last_jy);
14885
14886   for (i = 0; i < MAX_PLAYERS; i++)
14887     if (stored_player[i].active)
14888       found = TRUE;
14889
14890   if (!found)
14891     AllPlayersGone = TRUE;
14892
14893   ExitX = ZX = jx;
14894   ExitY = ZY = jy;
14895 }
14896
14897 #if USE_NEW_SNAP_DELAY
14898 static void setFieldForSnapping(int x, int y, int element, int direction)
14899 {
14900   struct ElementInfo *ei = &element_info[element];
14901   int direction_bit = MV_DIR_TO_BIT(direction);
14902   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14903   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14904                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14905
14906   Feld[x][y] = EL_ELEMENT_SNAPPING;
14907   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14908
14909   ResetGfxAnimation(x, y);
14910
14911   GfxElement[x][y] = element;
14912   GfxAction[x][y] = action;
14913   GfxDir[x][y] = direction;
14914   GfxFrame[x][y] = -1;
14915 }
14916 #endif
14917
14918 /*
14919   =============================================================================
14920   checkDiagonalPushing()
14921   -----------------------------------------------------------------------------
14922   check if diagonal input device direction results in pushing of object
14923   (by checking if the alternative direction is walkable, diggable, ...)
14924   =============================================================================
14925 */
14926
14927 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14928                                     int x, int y, int real_dx, int real_dy)
14929 {
14930   int jx, jy, dx, dy, xx, yy;
14931
14932   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14933     return TRUE;
14934
14935   /* diagonal direction: check alternative direction */
14936   jx = player->jx;
14937   jy = player->jy;
14938   dx = x - jx;
14939   dy = y - jy;
14940   xx = jx + (dx == 0 ? real_dx : 0);
14941   yy = jy + (dy == 0 ? real_dy : 0);
14942
14943   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14944 }
14945
14946 /*
14947   =============================================================================
14948   DigField()
14949   -----------------------------------------------------------------------------
14950   x, y:                 field next to player (non-diagonal) to try to dig to
14951   real_dx, real_dy:     direction as read from input device (can be diagonal)
14952   =============================================================================
14953 */
14954
14955 static int DigField(struct PlayerInfo *player,
14956                     int oldx, int oldy, int x, int y,
14957                     int real_dx, int real_dy, int mode)
14958 {
14959   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14960   boolean player_was_pushing = player->is_pushing;
14961   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14962   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14963   int jx = oldx, jy = oldy;
14964   int dx = x - jx, dy = y - jy;
14965   int nextx = x + dx, nexty = y + dy;
14966   int move_direction = (dx == -1 ? MV_LEFT  :
14967                         dx == +1 ? MV_RIGHT :
14968                         dy == -1 ? MV_UP    :
14969                         dy == +1 ? MV_DOWN  : MV_NONE);
14970   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14971   int dig_side = MV_DIR_OPPOSITE(move_direction);
14972   int old_element = Feld[jx][jy];
14973 #if USE_FIXED_DONT_RUN_INTO
14974   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14975 #else
14976   int element;
14977 #endif
14978   int collect_count;
14979
14980   if (is_player)                /* function can also be called by EL_PENGUIN */
14981   {
14982     if (player->MovPos == 0)
14983     {
14984       player->is_digging = FALSE;
14985       player->is_collecting = FALSE;
14986     }
14987
14988     if (player->MovPos == 0)    /* last pushing move finished */
14989       player->is_pushing = FALSE;
14990
14991     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14992     {
14993       player->is_switching = FALSE;
14994       player->push_delay = -1;
14995
14996       return MP_NO_ACTION;
14997     }
14998   }
14999
15000 #if !USE_FIXED_DONT_RUN_INTO
15001   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15002     return MP_NO_ACTION;
15003 #endif
15004
15005   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15006     old_element = Back[jx][jy];
15007
15008   /* in case of element dropped at player position, check background */
15009   else if (Back[jx][jy] != EL_EMPTY &&
15010            game.engine_version >= VERSION_IDENT(2,2,0,0))
15011     old_element = Back[jx][jy];
15012
15013   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15014     return MP_NO_ACTION;        /* field has no opening in this direction */
15015
15016   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15017     return MP_NO_ACTION;        /* field has no opening in this direction */
15018
15019 #if USE_FIXED_DONT_RUN_INTO
15020   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15021   {
15022     SplashAcid(x, y);
15023
15024     Feld[jx][jy] = player->artwork_element;
15025     InitMovingField(jx, jy, MV_DOWN);
15026     Store[jx][jy] = EL_ACID;
15027     ContinueMoving(jx, jy);
15028     BuryPlayer(player);
15029
15030     return MP_DONT_RUN_INTO;
15031   }
15032 #endif
15033
15034 #if USE_FIXED_DONT_RUN_INTO
15035   if (player_can_move && DONT_RUN_INTO(element))
15036   {
15037     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15038
15039     return MP_DONT_RUN_INTO;
15040   }
15041 #endif
15042
15043 #if USE_FIXED_DONT_RUN_INTO
15044   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15045     return MP_NO_ACTION;
15046 #endif
15047
15048 #if !USE_FIXED_DONT_RUN_INTO
15049   element = Feld[x][y];
15050 #endif
15051
15052   collect_count = element_info[element].collect_count_initial;
15053
15054   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15055     return MP_NO_ACTION;
15056
15057   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15058     player_can_move = player_can_move_or_snap;
15059
15060   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15061       game.engine_version >= VERSION_IDENT(2,2,0,0))
15062   {
15063     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15064                                player->index_bit, dig_side);
15065     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15066                                         player->index_bit, dig_side);
15067
15068     if (element == EL_DC_LANDMINE)
15069       Bang(x, y);
15070
15071     if (Feld[x][y] != element)          /* field changed by snapping */
15072       return MP_ACTION;
15073
15074     return MP_NO_ACTION;
15075   }
15076
15077 #if USE_PLAYER_GRAVITY
15078   if (player->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 #else
15083   if (game.gravity && is_player && !player->is_auto_moving &&
15084       canFallDown(player) && move_direction != MV_DOWN &&
15085       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15086     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15087 #endif
15088
15089   if (player_can_move &&
15090       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15091   {
15092     int sound_element = SND_ELEMENT(element);
15093     int sound_action = ACTION_WALKING;
15094
15095     if (IS_RND_GATE(element))
15096     {
15097       if (!player->key[RND_GATE_NR(element)])
15098         return MP_NO_ACTION;
15099     }
15100     else if (IS_RND_GATE_GRAY(element))
15101     {
15102       if (!player->key[RND_GATE_GRAY_NR(element)])
15103         return MP_NO_ACTION;
15104     }
15105     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15106     {
15107       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15108         return MP_NO_ACTION;
15109     }
15110     else if (element == EL_EXIT_OPEN ||
15111              element == EL_EM_EXIT_OPEN ||
15112 #if 1
15113              element == EL_EM_EXIT_OPENING ||
15114 #endif
15115              element == EL_STEEL_EXIT_OPEN ||
15116              element == EL_EM_STEEL_EXIT_OPEN ||
15117 #if 1
15118              element == EL_EM_STEEL_EXIT_OPENING ||
15119 #endif
15120              element == EL_SP_EXIT_OPEN ||
15121              element == EL_SP_EXIT_OPENING)
15122     {
15123       sound_action = ACTION_PASSING;    /* player is passing exit */
15124     }
15125     else if (element == EL_EMPTY)
15126     {
15127       sound_action = ACTION_MOVING;             /* nothing to walk on */
15128     }
15129
15130     /* play sound from background or player, whatever is available */
15131     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15132       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15133     else
15134       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15135   }
15136   else if (player_can_move &&
15137            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15138   {
15139     if (!ACCESS_FROM(element, opposite_direction))
15140       return MP_NO_ACTION;      /* field not accessible from this direction */
15141
15142     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15143       return MP_NO_ACTION;
15144
15145     if (IS_EM_GATE(element))
15146     {
15147       if (!player->key[EM_GATE_NR(element)])
15148         return MP_NO_ACTION;
15149     }
15150     else if (IS_EM_GATE_GRAY(element))
15151     {
15152       if (!player->key[EM_GATE_GRAY_NR(element)])
15153         return MP_NO_ACTION;
15154     }
15155     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15156     {
15157       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15158         return MP_NO_ACTION;
15159     }
15160     else if (IS_EMC_GATE(element))
15161     {
15162       if (!player->key[EMC_GATE_NR(element)])
15163         return MP_NO_ACTION;
15164     }
15165     else if (IS_EMC_GATE_GRAY(element))
15166     {
15167       if (!player->key[EMC_GATE_GRAY_NR(element)])
15168         return MP_NO_ACTION;
15169     }
15170     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15171     {
15172       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15173         return MP_NO_ACTION;
15174     }
15175     else if (element == EL_DC_GATE_WHITE ||
15176              element == EL_DC_GATE_WHITE_GRAY ||
15177              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15178     {
15179       if (player->num_white_keys == 0)
15180         return MP_NO_ACTION;
15181
15182       player->num_white_keys--;
15183     }
15184     else if (IS_SP_PORT(element))
15185     {
15186       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15187           element == EL_SP_GRAVITY_PORT_RIGHT ||
15188           element == EL_SP_GRAVITY_PORT_UP ||
15189           element == EL_SP_GRAVITY_PORT_DOWN)
15190 #if USE_PLAYER_GRAVITY
15191         player->gravity = !player->gravity;
15192 #else
15193         game.gravity = !game.gravity;
15194 #endif
15195       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15196                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15197                element == EL_SP_GRAVITY_ON_PORT_UP ||
15198                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15199 #if USE_PLAYER_GRAVITY
15200         player->gravity = TRUE;
15201 #else
15202         game.gravity = TRUE;
15203 #endif
15204       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15205                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15206                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15207                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15208 #if USE_PLAYER_GRAVITY
15209         player->gravity = FALSE;
15210 #else
15211         game.gravity = FALSE;
15212 #endif
15213     }
15214
15215     /* automatically move to the next field with double speed */
15216     player->programmed_action = move_direction;
15217
15218     if (player->move_delay_reset_counter == 0)
15219     {
15220       player->move_delay_reset_counter = 2;     /* two double speed steps */
15221
15222       DOUBLE_PLAYER_SPEED(player);
15223     }
15224
15225     PlayLevelSoundAction(x, y, ACTION_PASSING);
15226   }
15227   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15228   {
15229     RemoveField(x, y);
15230
15231     if (mode != DF_SNAP)
15232     {
15233       GfxElement[x][y] = GFX_ELEMENT(element);
15234       player->is_digging = TRUE;
15235     }
15236
15237     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15238
15239     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15240                                         player->index_bit, dig_side);
15241
15242     if (mode == DF_SNAP)
15243     {
15244 #if USE_NEW_SNAP_DELAY
15245       if (level.block_snap_field)
15246         setFieldForSnapping(x, y, element, move_direction);
15247       else
15248         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15249 #else
15250       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15251 #endif
15252
15253       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15254                                           player->index_bit, dig_side);
15255     }
15256   }
15257   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15258   {
15259     RemoveField(x, y);
15260
15261     if (is_player && mode != DF_SNAP)
15262     {
15263       GfxElement[x][y] = element;
15264       player->is_collecting = TRUE;
15265     }
15266
15267     if (element == EL_SPEED_PILL)
15268     {
15269       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15270     }
15271     else if (element == EL_EXTRA_TIME && level.time > 0)
15272     {
15273       TimeLeft += level.extra_time;
15274
15275 #if 1
15276       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15277
15278       DisplayGameControlValues();
15279 #else
15280       DrawGameValue_Time(TimeLeft);
15281 #endif
15282     }
15283     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15284     {
15285       player->shield_normal_time_left += level.shield_normal_time;
15286       if (element == EL_SHIELD_DEADLY)
15287         player->shield_deadly_time_left += level.shield_deadly_time;
15288     }
15289     else if (element == EL_DYNAMITE ||
15290              element == EL_EM_DYNAMITE ||
15291              element == EL_SP_DISK_RED)
15292     {
15293       if (player->inventory_size < MAX_INVENTORY_SIZE)
15294         player->inventory_element[player->inventory_size++] = element;
15295
15296       DrawGameDoorValues();
15297     }
15298     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15299     {
15300       player->dynabomb_count++;
15301       player->dynabombs_left++;
15302     }
15303     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15304     {
15305       player->dynabomb_size++;
15306     }
15307     else if (element == EL_DYNABOMB_INCREASE_POWER)
15308     {
15309       player->dynabomb_xl = TRUE;
15310     }
15311     else if (IS_KEY(element))
15312     {
15313       player->key[KEY_NR(element)] = TRUE;
15314
15315       DrawGameDoorValues();
15316     }
15317     else if (element == EL_DC_KEY_WHITE)
15318     {
15319       player->num_white_keys++;
15320
15321       /* display white keys? */
15322       /* DrawGameDoorValues(); */
15323     }
15324     else if (IS_ENVELOPE(element))
15325     {
15326       player->show_envelope = element;
15327     }
15328     else if (element == EL_EMC_LENSES)
15329     {
15330       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15331
15332       RedrawAllInvisibleElementsForLenses();
15333     }
15334     else if (element == EL_EMC_MAGNIFIER)
15335     {
15336       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15337
15338       RedrawAllInvisibleElementsForMagnifier();
15339     }
15340     else if (IS_DROPPABLE(element) ||
15341              IS_THROWABLE(element))     /* can be collected and dropped */
15342     {
15343       int i;
15344
15345       if (collect_count == 0)
15346         player->inventory_infinite_element = element;
15347       else
15348         for (i = 0; i < collect_count; i++)
15349           if (player->inventory_size < MAX_INVENTORY_SIZE)
15350             player->inventory_element[player->inventory_size++] = element;
15351
15352       DrawGameDoorValues();
15353     }
15354     else if (collect_count > 0)
15355     {
15356       local_player->gems_still_needed -= collect_count;
15357       if (local_player->gems_still_needed < 0)
15358         local_player->gems_still_needed = 0;
15359
15360 #if 1
15361       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15362
15363       DisplayGameControlValues();
15364 #else
15365       DrawGameValue_Emeralds(local_player->gems_still_needed);
15366 #endif
15367     }
15368
15369     RaiseScoreElement(element);
15370     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15371
15372     if (is_player)
15373       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15374                                           player->index_bit, dig_side);
15375
15376     if (mode == DF_SNAP)
15377     {
15378 #if USE_NEW_SNAP_DELAY
15379       if (level.block_snap_field)
15380         setFieldForSnapping(x, y, element, move_direction);
15381       else
15382         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15383 #else
15384       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15385 #endif
15386
15387       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15388                                           player->index_bit, dig_side);
15389     }
15390   }
15391   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15392   {
15393     if (mode == DF_SNAP && element != EL_BD_ROCK)
15394       return MP_NO_ACTION;
15395
15396     if (CAN_FALL(element) && dy)
15397       return MP_NO_ACTION;
15398
15399     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15400         !(element == EL_SPRING && level.use_spring_bug))
15401       return MP_NO_ACTION;
15402
15403     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15404         ((move_direction & MV_VERTICAL &&
15405           ((element_info[element].move_pattern & MV_LEFT &&
15406             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15407            (element_info[element].move_pattern & MV_RIGHT &&
15408             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15409          (move_direction & MV_HORIZONTAL &&
15410           ((element_info[element].move_pattern & MV_UP &&
15411             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15412            (element_info[element].move_pattern & MV_DOWN &&
15413             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15414       return MP_NO_ACTION;
15415
15416     /* do not push elements already moving away faster than player */
15417     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15418         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15419       return MP_NO_ACTION;
15420
15421     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15422     {
15423       if (player->push_delay_value == -1 || !player_was_pushing)
15424         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15425     }
15426     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15427     {
15428       if (player->push_delay_value == -1)
15429         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15430     }
15431     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15432     {
15433       if (!player->is_pushing)
15434         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15435     }
15436
15437     player->is_pushing = TRUE;
15438     player->is_active = TRUE;
15439
15440     if (!(IN_LEV_FIELD(nextx, nexty) &&
15441           (IS_FREE(nextx, nexty) ||
15442            (IS_SB_ELEMENT(element) &&
15443             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15444            (IS_CUSTOM_ELEMENT(element) &&
15445             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15446       return MP_NO_ACTION;
15447
15448     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15449       return MP_NO_ACTION;
15450
15451     if (player->push_delay == -1)       /* new pushing; restart delay */
15452       player->push_delay = 0;
15453
15454     if (player->push_delay < player->push_delay_value &&
15455         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15456         element != EL_SPRING && element != EL_BALLOON)
15457     {
15458       /* make sure that there is no move delay before next try to push */
15459       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15460         player->move_delay = 0;
15461
15462       return MP_NO_ACTION;
15463     }
15464
15465     if (IS_CUSTOM_ELEMENT(element) &&
15466         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15467     {
15468       if (!DigFieldByCE(nextx, nexty, element))
15469         return MP_NO_ACTION;
15470     }
15471
15472     if (IS_SB_ELEMENT(element))
15473     {
15474       if (element == EL_SOKOBAN_FIELD_FULL)
15475       {
15476         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15477         local_player->sokobanfields_still_needed++;
15478       }
15479
15480       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15481       {
15482         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15483         local_player->sokobanfields_still_needed--;
15484       }
15485
15486       Feld[x][y] = EL_SOKOBAN_OBJECT;
15487
15488       if (Back[x][y] == Back[nextx][nexty])
15489         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15490       else if (Back[x][y] != 0)
15491         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15492                                     ACTION_EMPTYING);
15493       else
15494         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15495                                     ACTION_FILLING);
15496
15497 #if 1
15498       if (local_player->sokobanfields_still_needed == 0 &&
15499           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15500 #else
15501       if (local_player->sokobanfields_still_needed == 0 &&
15502           game.emulation == EMU_SOKOBAN)
15503 #endif
15504       {
15505         PlayerWins(player);
15506
15507         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15508       }
15509     }
15510     else
15511       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15512
15513     InitMovingField(x, y, move_direction);
15514     GfxAction[x][y] = ACTION_PUSHING;
15515
15516     if (mode == DF_SNAP)
15517       ContinueMoving(x, y);
15518     else
15519       MovPos[x][y] = (dx != 0 ? dx : dy);
15520
15521     Pushed[x][y] = TRUE;
15522     Pushed[nextx][nexty] = TRUE;
15523
15524     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15525       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15526     else
15527       player->push_delay_value = -1;    /* get new value later */
15528
15529     /* check for element change _after_ element has been pushed */
15530     if (game.use_change_when_pushing_bug)
15531     {
15532       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15533                                  player->index_bit, dig_side);
15534       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15535                                           player->index_bit, dig_side);
15536     }
15537   }
15538   else if (IS_SWITCHABLE(element))
15539   {
15540     if (PLAYER_SWITCHING(player, x, y))
15541     {
15542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15543                                           player->index_bit, dig_side);
15544
15545       return MP_ACTION;
15546     }
15547
15548     player->is_switching = TRUE;
15549     player->switch_x = x;
15550     player->switch_y = y;
15551
15552     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15553
15554     if (element == EL_ROBOT_WHEEL)
15555     {
15556       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15557       ZX = x;
15558       ZY = y;
15559
15560       game.robot_wheel_active = TRUE;
15561
15562       TEST_DrawLevelField(x, y);
15563     }
15564     else if (element == EL_SP_TERMINAL)
15565     {
15566       int xx, yy;
15567
15568       SCAN_PLAYFIELD(xx, yy)
15569       {
15570         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15571           Bang(xx, yy);
15572         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15573           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15574       }
15575     }
15576     else if (IS_BELT_SWITCH(element))
15577     {
15578       ToggleBeltSwitch(x, y);
15579     }
15580     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15581              element == EL_SWITCHGATE_SWITCH_DOWN ||
15582              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15583              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15584     {
15585       ToggleSwitchgateSwitch(x, y);
15586     }
15587     else if (element == EL_LIGHT_SWITCH ||
15588              element == EL_LIGHT_SWITCH_ACTIVE)
15589     {
15590       ToggleLightSwitch(x, y);
15591     }
15592     else if (element == EL_TIMEGATE_SWITCH ||
15593              element == EL_DC_TIMEGATE_SWITCH)
15594     {
15595       ActivateTimegateSwitch(x, y);
15596     }
15597     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15598              element == EL_BALLOON_SWITCH_RIGHT ||
15599              element == EL_BALLOON_SWITCH_UP    ||
15600              element == EL_BALLOON_SWITCH_DOWN  ||
15601              element == EL_BALLOON_SWITCH_NONE  ||
15602              element == EL_BALLOON_SWITCH_ANY)
15603     {
15604       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15605                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15606                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15607                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15608                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15609                              move_direction);
15610     }
15611     else if (element == EL_LAMP)
15612     {
15613       Feld[x][y] = EL_LAMP_ACTIVE;
15614       local_player->lights_still_needed--;
15615
15616       ResetGfxAnimation(x, y);
15617       TEST_DrawLevelField(x, y);
15618     }
15619     else if (element == EL_TIME_ORB_FULL)
15620     {
15621       Feld[x][y] = EL_TIME_ORB_EMPTY;
15622
15623       if (level.time > 0 || level.use_time_orb_bug)
15624       {
15625         TimeLeft += level.time_orb_time;
15626
15627 #if 1
15628         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15629
15630         DisplayGameControlValues();
15631 #else
15632         DrawGameValue_Time(TimeLeft);
15633 #endif
15634       }
15635
15636       ResetGfxAnimation(x, y);
15637       TEST_DrawLevelField(x, y);
15638     }
15639     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15640              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15641     {
15642       int xx, yy;
15643
15644       game.ball_state = !game.ball_state;
15645
15646       SCAN_PLAYFIELD(xx, yy)
15647       {
15648         int e = Feld[xx][yy];
15649
15650         if (game.ball_state)
15651         {
15652           if (e == EL_EMC_MAGIC_BALL)
15653             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15654           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15655             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15656         }
15657         else
15658         {
15659           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15660             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15661           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15662             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15663         }
15664       }
15665     }
15666
15667     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15668                                         player->index_bit, dig_side);
15669
15670     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15671                                         player->index_bit, dig_side);
15672
15673     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15674                                         player->index_bit, dig_side);
15675
15676     return MP_ACTION;
15677   }
15678   else
15679   {
15680     if (!PLAYER_SWITCHING(player, x, y))
15681     {
15682       player->is_switching = TRUE;
15683       player->switch_x = x;
15684       player->switch_y = y;
15685
15686       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15687                                  player->index_bit, dig_side);
15688       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15689                                           player->index_bit, dig_side);
15690
15691       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15692                                  player->index_bit, dig_side);
15693       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15694                                           player->index_bit, dig_side);
15695     }
15696
15697     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15698                                player->index_bit, dig_side);
15699     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15700                                         player->index_bit, dig_side);
15701
15702     return MP_NO_ACTION;
15703   }
15704
15705   player->push_delay = -1;
15706
15707   if (is_player)                /* function can also be called by EL_PENGUIN */
15708   {
15709     if (Feld[x][y] != element)          /* really digged/collected something */
15710     {
15711       player->is_collecting = !player->is_digging;
15712       player->is_active = TRUE;
15713     }
15714   }
15715
15716   return MP_MOVING;
15717 }
15718
15719 static boolean DigFieldByCE(int x, int y, int digging_element)
15720 {
15721   int element = Feld[x][y];
15722
15723   if (!IS_FREE(x, y))
15724   {
15725     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15726                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15727                   ACTION_BREAKING);
15728
15729     /* no element can dig solid indestructible elements */
15730     if (IS_INDESTRUCTIBLE(element) &&
15731         !IS_DIGGABLE(element) &&
15732         !IS_COLLECTIBLE(element))
15733       return FALSE;
15734
15735     if (AmoebaNr[x][y] &&
15736         (element == EL_AMOEBA_FULL ||
15737          element == EL_BD_AMOEBA ||
15738          element == EL_AMOEBA_GROWING))
15739     {
15740       AmoebaCnt[AmoebaNr[x][y]]--;
15741       AmoebaCnt2[AmoebaNr[x][y]]--;
15742     }
15743
15744     if (IS_MOVING(x, y))
15745       RemoveMovingField(x, y);
15746     else
15747     {
15748       RemoveField(x, y);
15749       TEST_DrawLevelField(x, y);
15750     }
15751
15752     /* if digged element was about to explode, prevent the explosion */
15753     ExplodeField[x][y] = EX_TYPE_NONE;
15754
15755     PlayLevelSoundAction(x, y, action);
15756   }
15757
15758   Store[x][y] = EL_EMPTY;
15759
15760 #if 1
15761   /* this makes it possible to leave the removed element again */
15762   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15763     Store[x][y] = element;
15764 #else
15765   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15766   {
15767     int move_leave_element = element_info[digging_element].move_leave_element;
15768
15769     /* this makes it possible to leave the removed element again */
15770     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15771                    element : move_leave_element);
15772   }
15773 #endif
15774
15775   return TRUE;
15776 }
15777
15778 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15779 {
15780   int jx = player->jx, jy = player->jy;
15781   int x = jx + dx, y = jy + dy;
15782   int snap_direction = (dx == -1 ? MV_LEFT  :
15783                         dx == +1 ? MV_RIGHT :
15784                         dy == -1 ? MV_UP    :
15785                         dy == +1 ? MV_DOWN  : MV_NONE);
15786   boolean can_continue_snapping = (level.continuous_snapping &&
15787                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15788
15789   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15790     return FALSE;
15791
15792   if (!player->active || !IN_LEV_FIELD(x, y))
15793     return FALSE;
15794
15795   if (dx && dy)
15796     return FALSE;
15797
15798   if (!dx && !dy)
15799   {
15800     if (player->MovPos == 0)
15801       player->is_pushing = FALSE;
15802
15803     player->is_snapping = FALSE;
15804
15805     if (player->MovPos == 0)
15806     {
15807       player->is_moving = FALSE;
15808       player->is_digging = FALSE;
15809       player->is_collecting = FALSE;
15810     }
15811
15812     return FALSE;
15813   }
15814
15815 #if USE_NEW_CONTINUOUS_SNAPPING
15816   /* prevent snapping with already pressed snap key when not allowed */
15817   if (player->is_snapping && !can_continue_snapping)
15818     return FALSE;
15819 #else
15820   if (player->is_snapping)
15821     return FALSE;
15822 #endif
15823
15824   player->MovDir = snap_direction;
15825
15826   if (player->MovPos == 0)
15827   {
15828     player->is_moving = FALSE;
15829     player->is_digging = FALSE;
15830     player->is_collecting = FALSE;
15831   }
15832
15833   player->is_dropping = FALSE;
15834   player->is_dropping_pressed = FALSE;
15835   player->drop_pressed_delay = 0;
15836
15837   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15838     return FALSE;
15839
15840   player->is_snapping = TRUE;
15841   player->is_active = TRUE;
15842
15843   if (player->MovPos == 0)
15844   {
15845     player->is_moving = FALSE;
15846     player->is_digging = FALSE;
15847     player->is_collecting = FALSE;
15848   }
15849
15850   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15851     TEST_DrawLevelField(player->last_jx, player->last_jy);
15852
15853   TEST_DrawLevelField(x, y);
15854
15855   return TRUE;
15856 }
15857
15858 static boolean DropElement(struct PlayerInfo *player)
15859 {
15860   int old_element, new_element;
15861   int dropx = player->jx, dropy = player->jy;
15862   int drop_direction = player->MovDir;
15863   int drop_side = drop_direction;
15864 #if 1
15865   int drop_element = get_next_dropped_element(player);
15866 #else
15867   int drop_element = (player->inventory_size > 0 ?
15868                       player->inventory_element[player->inventory_size - 1] :
15869                       player->inventory_infinite_element != EL_UNDEFINED ?
15870                       player->inventory_infinite_element :
15871                       player->dynabombs_left > 0 ?
15872                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15873                       EL_UNDEFINED);
15874 #endif
15875
15876   player->is_dropping_pressed = TRUE;
15877
15878   /* do not drop an element on top of another element; when holding drop key
15879      pressed without moving, dropped element must move away before the next
15880      element can be dropped (this is especially important if the next element
15881      is dynamite, which can be placed on background for historical reasons) */
15882   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15883     return MP_ACTION;
15884
15885   if (IS_THROWABLE(drop_element))
15886   {
15887     dropx += GET_DX_FROM_DIR(drop_direction);
15888     dropy += GET_DY_FROM_DIR(drop_direction);
15889
15890     if (!IN_LEV_FIELD(dropx, dropy))
15891       return FALSE;
15892   }
15893
15894   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15895   new_element = drop_element;           /* default: no change when dropping */
15896
15897   /* check if player is active, not moving and ready to drop */
15898   if (!player->active || player->MovPos || player->drop_delay > 0)
15899     return FALSE;
15900
15901   /* check if player has anything that can be dropped */
15902   if (new_element == EL_UNDEFINED)
15903     return FALSE;
15904
15905   /* check if drop key was pressed long enough for EM style dynamite */
15906   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15907     return FALSE;
15908
15909   /* check if anything can be dropped at the current position */
15910   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15911     return FALSE;
15912
15913   /* collected custom elements can only be dropped on empty fields */
15914   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15915     return FALSE;
15916
15917   if (old_element != EL_EMPTY)
15918     Back[dropx][dropy] = old_element;   /* store old element on this field */
15919
15920   ResetGfxAnimation(dropx, dropy);
15921   ResetRandomAnimationValue(dropx, dropy);
15922
15923   if (player->inventory_size > 0 ||
15924       player->inventory_infinite_element != EL_UNDEFINED)
15925   {
15926     if (player->inventory_size > 0)
15927     {
15928       player->inventory_size--;
15929
15930       DrawGameDoorValues();
15931
15932       if (new_element == EL_DYNAMITE)
15933         new_element = EL_DYNAMITE_ACTIVE;
15934       else if (new_element == EL_EM_DYNAMITE)
15935         new_element = EL_EM_DYNAMITE_ACTIVE;
15936       else if (new_element == EL_SP_DISK_RED)
15937         new_element = EL_SP_DISK_RED_ACTIVE;
15938     }
15939
15940     Feld[dropx][dropy] = new_element;
15941
15942     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15943       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15944                           el2img(Feld[dropx][dropy]), 0);
15945
15946     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15947
15948     /* needed if previous element just changed to "empty" in the last frame */
15949     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15950
15951     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15952                                player->index_bit, drop_side);
15953     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15954                                         CE_PLAYER_DROPS_X,
15955                                         player->index_bit, drop_side);
15956
15957     TestIfElementTouchesCustomElement(dropx, dropy);
15958   }
15959   else          /* player is dropping a dyna bomb */
15960   {
15961     player->dynabombs_left--;
15962
15963     Feld[dropx][dropy] = new_element;
15964
15965     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15966       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15967                           el2img(Feld[dropx][dropy]), 0);
15968
15969     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15970   }
15971
15972   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15973     InitField_WithBug1(dropx, dropy, FALSE);
15974
15975   new_element = Feld[dropx][dropy];     /* element might have changed */
15976
15977   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15978       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15979   {
15980     int move_direction, nextx, nexty;
15981
15982     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15983       MovDir[dropx][dropy] = drop_direction;
15984
15985     move_direction = MovDir[dropx][dropy];
15986     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15987     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15988
15989     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15990
15991 #if USE_FIX_IMPACT_COLLISION
15992     /* do not cause impact style collision by dropping elements that can fall */
15993     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15994 #else
15995     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15996 #endif
15997   }
15998
15999   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16000   player->is_dropping = TRUE;
16001
16002   player->drop_pressed_delay = 0;
16003   player->is_dropping_pressed = FALSE;
16004
16005   player->drop_x = dropx;
16006   player->drop_y = dropy;
16007
16008   return TRUE;
16009 }
16010
16011 /* ------------------------------------------------------------------------- */
16012 /* game sound playing functions                                              */
16013 /* ------------------------------------------------------------------------- */
16014
16015 static int *loop_sound_frame = NULL;
16016 static int *loop_sound_volume = NULL;
16017
16018 void InitPlayLevelSound()
16019 {
16020   int num_sounds = getSoundListSize();
16021
16022   checked_free(loop_sound_frame);
16023   checked_free(loop_sound_volume);
16024
16025   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16026   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16027 }
16028
16029 static void PlayLevelSound(int x, int y, int nr)
16030 {
16031   int sx = SCREENX(x), sy = SCREENY(y);
16032   int volume, stereo_position;
16033   int max_distance = 8;
16034   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16035
16036   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16037       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16038     return;
16039
16040   if (!IN_LEV_FIELD(x, y) ||
16041       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16042       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16043     return;
16044
16045   volume = SOUND_MAX_VOLUME;
16046
16047   if (!IN_SCR_FIELD(sx, sy))
16048   {
16049     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16050     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16051
16052     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16053   }
16054
16055   stereo_position = (SOUND_MAX_LEFT +
16056                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16057                      (SCR_FIELDX + 2 * max_distance));
16058
16059   if (IS_LOOP_SOUND(nr))
16060   {
16061     /* This assures that quieter loop sounds do not overwrite louder ones,
16062        while restarting sound volume comparison with each new game frame. */
16063
16064     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16065       return;
16066
16067     loop_sound_volume[nr] = volume;
16068     loop_sound_frame[nr] = FrameCounter;
16069   }
16070
16071   PlaySoundExt(nr, volume, stereo_position, type);
16072 }
16073
16074 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16075 {
16076   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16077                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16078                  y < LEVELY(BY1) ? LEVELY(BY1) :
16079                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16080                  sound_action);
16081 }
16082
16083 static void PlayLevelSoundAction(int x, int y, int action)
16084 {
16085   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16086 }
16087
16088 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16089 {
16090   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16091
16092   if (sound_effect != SND_UNDEFINED)
16093     PlayLevelSound(x, y, sound_effect);
16094 }
16095
16096 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16097                                               int action)
16098 {
16099   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16100
16101   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16102     PlayLevelSound(x, y, sound_effect);
16103 }
16104
16105 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16106 {
16107   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16108
16109   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16110     PlayLevelSound(x, y, sound_effect);
16111 }
16112
16113 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16114 {
16115   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16116
16117   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16118     StopSound(sound_effect);
16119 }
16120
16121 static void PlayLevelMusic()
16122 {
16123   if (levelset.music[level_nr] != MUS_UNDEFINED)
16124     PlayMusic(levelset.music[level_nr]);        /* from config file */
16125   else
16126     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16127 }
16128
16129 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16130 {
16131   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16132   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16133   int x = xx - 1 - offset;
16134   int y = yy - 1 - offset;
16135
16136   switch (sample)
16137   {
16138     case SAMPLE_blank:
16139       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16140       break;
16141
16142     case SAMPLE_roll:
16143       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16144       break;
16145
16146     case SAMPLE_stone:
16147       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16148       break;
16149
16150     case SAMPLE_nut:
16151       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16152       break;
16153
16154     case SAMPLE_crack:
16155       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16156       break;
16157
16158     case SAMPLE_bug:
16159       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16160       break;
16161
16162     case SAMPLE_tank:
16163       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16164       break;
16165
16166     case SAMPLE_android_clone:
16167       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16168       break;
16169
16170     case SAMPLE_android_move:
16171       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16172       break;
16173
16174     case SAMPLE_spring:
16175       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16176       break;
16177
16178     case SAMPLE_slurp:
16179       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16180       break;
16181
16182     case SAMPLE_eater:
16183       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16184       break;
16185
16186     case SAMPLE_eater_eat:
16187       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16188       break;
16189
16190     case SAMPLE_alien:
16191       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16192       break;
16193
16194     case SAMPLE_collect:
16195       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16196       break;
16197
16198     case SAMPLE_diamond:
16199       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16200       break;
16201
16202     case SAMPLE_squash:
16203       /* !!! CHECK THIS !!! */
16204 #if 1
16205       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16206 #else
16207       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16208 #endif
16209       break;
16210
16211     case SAMPLE_wonderfall:
16212       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16213       break;
16214
16215     case SAMPLE_drip:
16216       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16217       break;
16218
16219     case SAMPLE_push:
16220       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16221       break;
16222
16223     case SAMPLE_dirt:
16224       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16225       break;
16226
16227     case SAMPLE_acid:
16228       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16229       break;
16230
16231     case SAMPLE_ball:
16232       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16233       break;
16234
16235     case SAMPLE_grow:
16236       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16237       break;
16238
16239     case SAMPLE_wonder:
16240       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16241       break;
16242
16243     case SAMPLE_door:
16244       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16245       break;
16246
16247     case SAMPLE_exit_open:
16248       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16249       break;
16250
16251     case SAMPLE_exit_leave:
16252       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16253       break;
16254
16255     case SAMPLE_dynamite:
16256       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16257       break;
16258
16259     case SAMPLE_tick:
16260       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16261       break;
16262
16263     case SAMPLE_press:
16264       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16265       break;
16266
16267     case SAMPLE_wheel:
16268       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16269       break;
16270
16271     case SAMPLE_boom:
16272       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16273       break;
16274
16275     case SAMPLE_die:
16276       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16277       break;
16278
16279     case SAMPLE_time:
16280       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16281       break;
16282
16283     default:
16284       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16285       break;
16286   }
16287 }
16288
16289 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16290 {
16291   int element = map_element_SP_to_RND(element_sp);
16292   int action = map_action_SP_to_RND(action_sp);
16293   int offset = (setup.sp_show_border_elements ? 0 : 1);
16294   int x = xx - offset;
16295   int y = yy - offset;
16296
16297 #if 0
16298   printf("::: %d -> %d\n", element_sp, action_sp);
16299 #endif
16300
16301   PlayLevelSoundElementAction(x, y, element, action);
16302 }
16303
16304 #if 0
16305 void ChangeTime(int value)
16306 {
16307   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16308
16309   *time += value;
16310
16311   /* EMC game engine uses value from time counter of RND game engine */
16312   level.native_em_level->lev->time = *time;
16313
16314   DrawGameValue_Time(*time);
16315 }
16316
16317 void RaiseScore(int value)
16318 {
16319   /* EMC game engine and RND game engine have separate score counters */
16320   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16321                 &level.native_em_level->lev->score : &local_player->score);
16322
16323   *score += value;
16324
16325   DrawGameValue_Score(*score);
16326 }
16327 #endif
16328
16329 void RaiseScore(int value)
16330 {
16331   local_player->score += value;
16332
16333 #if 1
16334   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16335
16336   DisplayGameControlValues();
16337 #else
16338   DrawGameValue_Score(local_player->score);
16339 #endif
16340 }
16341
16342 void RaiseScoreElement(int element)
16343 {
16344   switch (element)
16345   {
16346     case EL_EMERALD:
16347     case EL_BD_DIAMOND:
16348     case EL_EMERALD_YELLOW:
16349     case EL_EMERALD_RED:
16350     case EL_EMERALD_PURPLE:
16351     case EL_SP_INFOTRON:
16352       RaiseScore(level.score[SC_EMERALD]);
16353       break;
16354     case EL_DIAMOND:
16355       RaiseScore(level.score[SC_DIAMOND]);
16356       break;
16357     case EL_CRYSTAL:
16358       RaiseScore(level.score[SC_CRYSTAL]);
16359       break;
16360     case EL_PEARL:
16361       RaiseScore(level.score[SC_PEARL]);
16362       break;
16363     case EL_BUG:
16364     case EL_BD_BUTTERFLY:
16365     case EL_SP_ELECTRON:
16366       RaiseScore(level.score[SC_BUG]);
16367       break;
16368     case EL_SPACESHIP:
16369     case EL_BD_FIREFLY:
16370     case EL_SP_SNIKSNAK:
16371       RaiseScore(level.score[SC_SPACESHIP]);
16372       break;
16373     case EL_YAMYAM:
16374     case EL_DARK_YAMYAM:
16375       RaiseScore(level.score[SC_YAMYAM]);
16376       break;
16377     case EL_ROBOT:
16378       RaiseScore(level.score[SC_ROBOT]);
16379       break;
16380     case EL_PACMAN:
16381       RaiseScore(level.score[SC_PACMAN]);
16382       break;
16383     case EL_NUT:
16384       RaiseScore(level.score[SC_NUT]);
16385       break;
16386     case EL_DYNAMITE:
16387     case EL_EM_DYNAMITE:
16388     case EL_SP_DISK_RED:
16389     case EL_DYNABOMB_INCREASE_NUMBER:
16390     case EL_DYNABOMB_INCREASE_SIZE:
16391     case EL_DYNABOMB_INCREASE_POWER:
16392       RaiseScore(level.score[SC_DYNAMITE]);
16393       break;
16394     case EL_SHIELD_NORMAL:
16395     case EL_SHIELD_DEADLY:
16396       RaiseScore(level.score[SC_SHIELD]);
16397       break;
16398     case EL_EXTRA_TIME:
16399       RaiseScore(level.extra_time_score);
16400       break;
16401     case EL_KEY_1:
16402     case EL_KEY_2:
16403     case EL_KEY_3:
16404     case EL_KEY_4:
16405     case EL_EM_KEY_1:
16406     case EL_EM_KEY_2:
16407     case EL_EM_KEY_3:
16408     case EL_EM_KEY_4:
16409     case EL_EMC_KEY_5:
16410     case EL_EMC_KEY_6:
16411     case EL_EMC_KEY_7:
16412     case EL_EMC_KEY_8:
16413     case EL_DC_KEY_WHITE:
16414       RaiseScore(level.score[SC_KEY]);
16415       break;
16416     default:
16417       RaiseScore(element_info[element].collect_score);
16418       break;
16419   }
16420 }
16421
16422 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16423 {
16424   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16425   {
16426 #if defined(NETWORK_AVALIABLE)
16427     if (options.network)
16428       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16429     else
16430 #endif
16431     {
16432       if (quick_quit)
16433       {
16434 #if 1
16435
16436 #if 1
16437         FadeSkipNextFadeIn();
16438 #else
16439         fading = fading_none;
16440 #endif
16441
16442 #else
16443         OpenDoor(DOOR_CLOSE_1);
16444 #endif
16445
16446         game_status = GAME_MODE_MAIN;
16447
16448 #if 1
16449         DrawAndFadeInMainMenu(REDRAW_FIELD);
16450 #else
16451         DrawMainMenu();
16452 #endif
16453       }
16454       else
16455       {
16456 #if 0
16457         FadeOut(REDRAW_FIELD);
16458 #endif
16459
16460         game_status = GAME_MODE_MAIN;
16461
16462         DrawAndFadeInMainMenu(REDRAW_FIELD);
16463       }
16464     }
16465   }
16466   else          /* continue playing the game */
16467   {
16468     if (tape.playing && tape.deactivate_display)
16469       TapeDeactivateDisplayOff(TRUE);
16470
16471     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16472
16473     if (tape.playing && tape.deactivate_display)
16474       TapeDeactivateDisplayOn();
16475   }
16476 }
16477
16478 void RequestQuitGame(boolean ask_if_really_quit)
16479 {
16480   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16481   boolean skip_request = AllPlayersGone || quick_quit;
16482
16483   RequestQuitGameExt(skip_request, quick_quit,
16484                      "Do you really want to quit the game ?");
16485 }
16486
16487
16488 /* ------------------------------------------------------------------------- */
16489 /* random generator functions                                                */
16490 /* ------------------------------------------------------------------------- */
16491
16492 unsigned int InitEngineRandom_RND(long seed)
16493 {
16494   game.num_random_calls = 0;
16495
16496 #if 0
16497   unsigned int rnd_seed = InitEngineRandom(seed);
16498
16499   printf("::: START RND: %d\n", rnd_seed);
16500
16501   return rnd_seed;
16502 #else
16503
16504   return InitEngineRandom(seed);
16505
16506 #endif
16507
16508 }
16509
16510 unsigned int RND(int max)
16511 {
16512   if (max > 0)
16513   {
16514     game.num_random_calls++;
16515
16516     return GetEngineRandom(max);
16517   }
16518
16519   return 0;
16520 }
16521
16522
16523 /* ------------------------------------------------------------------------- */
16524 /* game engine snapshot handling functions                                   */
16525 /* ------------------------------------------------------------------------- */
16526
16527 struct EngineSnapshotInfo
16528 {
16529   /* runtime values for custom element collect score */
16530   int collect_score[NUM_CUSTOM_ELEMENTS];
16531
16532   /* runtime values for group element choice position */
16533   int choice_pos[NUM_GROUP_ELEMENTS];
16534
16535   /* runtime values for belt position animations */
16536   int belt_graphic[4][NUM_BELT_PARTS];
16537   int belt_anim_mode[4][NUM_BELT_PARTS];
16538 };
16539
16540 static struct EngineSnapshotInfo engine_snapshot_rnd;
16541 static char *snapshot_level_identifier = NULL;
16542 static int snapshot_level_nr = -1;
16543
16544 static void SaveEngineSnapshotValues_RND()
16545 {
16546   static int belt_base_active_element[4] =
16547   {
16548     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16549     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16550     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16551     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16552   };
16553   int i, j;
16554
16555   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16556   {
16557     int element = EL_CUSTOM_START + i;
16558
16559     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16560   }
16561
16562   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16563   {
16564     int element = EL_GROUP_START + i;
16565
16566     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16567   }
16568
16569   for (i = 0; i < 4; i++)
16570   {
16571     for (j = 0; j < NUM_BELT_PARTS; j++)
16572     {
16573       int element = belt_base_active_element[i] + j;
16574       int graphic = el2img(element);
16575       int anim_mode = graphic_info[graphic].anim_mode;
16576
16577       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16578       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16579     }
16580   }
16581 }
16582
16583 static void LoadEngineSnapshotValues_RND()
16584 {
16585   unsigned long num_random_calls = game.num_random_calls;
16586   int i, j;
16587
16588   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16589   {
16590     int element = EL_CUSTOM_START + i;
16591
16592     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16593   }
16594
16595   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16596   {
16597     int element = EL_GROUP_START + i;
16598
16599     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16600   }
16601
16602   for (i = 0; i < 4; i++)
16603   {
16604     for (j = 0; j < NUM_BELT_PARTS; j++)
16605     {
16606       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16607       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16608
16609       graphic_info[graphic].anim_mode = anim_mode;
16610     }
16611   }
16612
16613   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16614   {
16615     InitRND(tape.random_seed);
16616     for (i = 0; i < num_random_calls; i++)
16617       RND(1);
16618   }
16619
16620   if (game.num_random_calls != num_random_calls)
16621   {
16622     Error(ERR_INFO, "number of random calls out of sync");
16623     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16624     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16625     Error(ERR_EXIT, "this should not happen -- please debug");
16626   }
16627 }
16628
16629 void SaveEngineSnapshot()
16630 {
16631   /* do not save snapshots from editor */
16632   if (level_editor_test_game)
16633     return;
16634
16635   /* free previous snapshot buffers, if needed */
16636   FreeEngineSnapshotBuffers();
16637
16638   /* copy some special values to a structure better suited for the snapshot */
16639
16640   SaveEngineSnapshotValues_RND();
16641   SaveEngineSnapshotValues_EM();
16642   SaveEngineSnapshotValues_SP();
16643
16644   /* save values stored in special snapshot structure */
16645
16646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16649
16650   /* save further RND engine values */
16651
16652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16655
16656   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16657   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16658   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16659   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16660
16661   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16662   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16665   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16666
16667   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16670
16671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16672
16673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16674
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16677
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16694   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16696
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16699
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16703
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16706
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16712
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16715
16716   /* save level identification information */
16717
16718   setString(&snapshot_level_identifier, leveldir_current->identifier);
16719   snapshot_level_nr = level_nr;
16720
16721 #if 0
16722   ListNode *node = engine_snapshot_list_rnd;
16723   int num_bytes = 0;
16724
16725   while (node != NULL)
16726   {
16727     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16728
16729     node = node->next;
16730   }
16731
16732   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16733 #endif
16734 }
16735
16736 void LoadEngineSnapshot()
16737 {
16738   /* restore generically stored snapshot buffers */
16739
16740   LoadEngineSnapshotBuffers();
16741
16742   /* restore special values from snapshot structure */
16743
16744   LoadEngineSnapshotValues_RND();
16745   LoadEngineSnapshotValues_EM();
16746   LoadEngineSnapshotValues_SP();
16747 }
16748
16749 boolean CheckEngineSnapshot()
16750 {
16751   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16752           snapshot_level_nr == level_nr);
16753 }
16754
16755
16756 /* ---------- new game button stuff ---------------------------------------- */
16757
16758 static struct
16759 {
16760   int graphic;
16761   struct Rect *pos;
16762   int gadget_id;
16763   char *infotext;
16764 } gamebutton_info[NUM_GAME_BUTTONS] =
16765 {
16766   {
16767     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16768     GAME_CTRL_ID_STOP,                  "stop game"
16769   },
16770   {
16771     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16772     GAME_CTRL_ID_PAUSE,                 "pause game"
16773   },
16774   {
16775     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16776     GAME_CTRL_ID_PLAY,                  "play game"
16777   },
16778   {
16779     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16780     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16781   },
16782   {
16783     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16784     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16785   },
16786   {
16787     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16788     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16789   }
16790 };
16791
16792 void CreateGameButtons()
16793 {
16794   int i;
16795
16796   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16797   {
16798     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16799     struct Rect *pos = gamebutton_info[i].pos;
16800     struct GadgetInfo *gi;
16801     int button_type;
16802     boolean checked;
16803     unsigned long event_mask;
16804     int gd_x   = gfx->src_x;
16805     int gd_y   = gfx->src_y;
16806     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16807     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16808     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16809     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16810     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16811     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16812     int id = i;
16813
16814     if (id == GAME_CTRL_ID_STOP ||
16815         id == GAME_CTRL_ID_PAUSE ||
16816         id == GAME_CTRL_ID_PLAY)
16817     {
16818       button_type = GD_TYPE_NORMAL_BUTTON;
16819       checked = FALSE;
16820       event_mask = GD_EVENT_RELEASED;
16821     }
16822     else
16823     {
16824       button_type = GD_TYPE_CHECK_BUTTON;
16825       checked =
16826         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16827          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16828          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16829       event_mask = GD_EVENT_PRESSED;
16830     }
16831
16832     gi = CreateGadget(GDI_CUSTOM_ID, id,
16833                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16834                       GDI_X, DX + pos->x,
16835                       GDI_Y, DY + pos->y,
16836                       GDI_WIDTH, gfx->width,
16837                       GDI_HEIGHT, gfx->height,
16838                       GDI_TYPE, button_type,
16839                       GDI_STATE, GD_BUTTON_UNPRESSED,
16840                       GDI_CHECKED, checked,
16841                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16842                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16843                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16844                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16845                       GDI_DIRECT_DRAW, FALSE,
16846                       GDI_EVENT_MASK, event_mask,
16847                       GDI_CALLBACK_ACTION, HandleGameButtons,
16848                       GDI_END);
16849
16850     if (gi == NULL)
16851       Error(ERR_EXIT, "cannot create gadget");
16852
16853     game_gadget[id] = gi;
16854   }
16855 }
16856
16857 void FreeGameButtons()
16858 {
16859   int i;
16860
16861   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862     FreeGadget(game_gadget[i]);
16863 }
16864
16865 static void MapGameButtons()
16866 {
16867   int i;
16868
16869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870     MapGadget(game_gadget[i]);
16871 }
16872
16873 void UnmapGameButtons()
16874 {
16875   int i;
16876
16877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878     UnmapGadget(game_gadget[i]);
16879 }
16880
16881 void RedrawGameButtons()
16882 {
16883   int i;
16884
16885   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886     RedrawGadget(game_gadget[i]);
16887 }
16888
16889 static void HandleGameButtonsExt(int id)
16890 {
16891   if (game_status != GAME_MODE_PLAYING)
16892     return;
16893
16894   switch (id)
16895   {
16896     case GAME_CTRL_ID_STOP:
16897       if (tape.playing)
16898         TapeStop();
16899       else
16900         RequestQuitGame(TRUE);
16901       break;
16902
16903     case GAME_CTRL_ID_PAUSE:
16904       if (options.network)
16905       {
16906 #if defined(NETWORK_AVALIABLE)
16907         if (tape.pausing)
16908           SendToServer_ContinuePlaying();
16909         else
16910           SendToServer_PausePlaying();
16911 #endif
16912       }
16913       else
16914         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16915       break;
16916
16917     case GAME_CTRL_ID_PLAY:
16918       if (tape.pausing)
16919       {
16920 #if defined(NETWORK_AVALIABLE)
16921         if (options.network)
16922           SendToServer_ContinuePlaying();
16923         else
16924 #endif
16925         {
16926           tape.pausing = FALSE;
16927           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16928         }
16929       }
16930       break;
16931
16932     case SOUND_CTRL_ID_MUSIC:
16933       if (setup.sound_music)
16934       { 
16935         setup.sound_music = FALSE;
16936
16937         FadeMusic();
16938       }
16939       else if (audio.music_available)
16940       { 
16941         setup.sound = setup.sound_music = TRUE;
16942
16943         SetAudioMode(setup.sound);
16944
16945         PlayLevelMusic();
16946       }
16947       break;
16948
16949     case SOUND_CTRL_ID_LOOPS:
16950       if (setup.sound_loops)
16951         setup.sound_loops = FALSE;
16952       else if (audio.loops_available)
16953       {
16954         setup.sound = setup.sound_loops = TRUE;
16955
16956         SetAudioMode(setup.sound);
16957       }
16958       break;
16959
16960     case SOUND_CTRL_ID_SIMPLE:
16961       if (setup.sound_simple)
16962         setup.sound_simple = FALSE;
16963       else if (audio.sound_available)
16964       {
16965         setup.sound = setup.sound_simple = TRUE;
16966
16967         SetAudioMode(setup.sound);
16968       }
16969       break;
16970
16971     default:
16972       break;
16973   }
16974 }
16975
16976 static void HandleGameButtons(struct GadgetInfo *gi)
16977 {
16978   HandleGameButtonsExt(gi->custom_id);
16979 }
16980
16981 void HandleSoundButtonKeys(Key key)
16982 {
16983 #if 1
16984   if (key == setup.shortcut.sound_simple)
16985     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16986   else if (key == setup.shortcut.sound_loops)
16987     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16988   else if (key == setup.shortcut.sound_music)
16989     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16990 #else
16991   if (key == setup.shortcut.sound_simple)
16992     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16993   else if (key == setup.shortcut.sound_loops)
16994     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16995   else if (key == setup.shortcut.sound_music)
16996     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16997 #endif
16998 }