723143690961eda9c7c08ae37d8264d068b0ee69
[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
63 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
64
65 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
66
67 #if USE_DELAYED_GFX_REDRAW
68 #define TEST_DrawLevelField(x, y)                               \
69         GfxRedraw[x][y] |= GFX_REDRAW_TILE
70 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
71         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
72 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
73         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
74 #define TEST_DrawTwinkleOnField(x, y)                           \
75         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
76 #else
77 #define TEST_DrawLevelField(x, y)                               \
78              DrawLevelField(x, y)
79 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
80              DrawLevelFieldCrumbledSand(x, y)
81 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
82              DrawLevelFieldCrumbledSandNeighbours(x, y)
83 #define TEST_DrawTwinkleOnField(x, y)                           \
84              DrawTwinkleOnField(x, y)
85 #endif
86
87
88 /* for DigField() */
89 #define DF_NO_PUSH              0
90 #define DF_DIG                  1
91 #define DF_SNAP                 2
92
93 /* for MovePlayer() */
94 #define MP_NO_ACTION            0
95 #define MP_MOVING               1
96 #define MP_ACTION               2
97 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
98
99 /* for ScrollPlayer() */
100 #define SCROLL_INIT             0
101 #define SCROLL_GO_ON            1
102
103 /* for Bang()/Explode() */
104 #define EX_PHASE_START          0
105 #define EX_TYPE_NONE            0
106 #define EX_TYPE_NORMAL          (1 << 0)
107 #define EX_TYPE_CENTER          (1 << 1)
108 #define EX_TYPE_BORDER          (1 << 2)
109 #define EX_TYPE_CROSS           (1 << 3)
110 #define EX_TYPE_DYNA            (1 << 4)
111 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
112
113 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
114 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
115 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
116 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
117
118 /* special positions in the game control window (relative to control window) */
119 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
120 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
121 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
122 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
123 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
124 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
125 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
126 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
127 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
128 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
129 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
130 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
131 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
132 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
133 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
134 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
135
136 /* special positions in the game control window (relative to main window) */
137 #define DX_LEVEL1               (DX + XX_LEVEL1)
138 #define DX_LEVEL2               (DX + XX_LEVEL2)
139 #define DX_LEVEL                (DX + XX_LEVEL)
140 #define DY_LEVEL                (DY + YY_LEVEL)
141 #define DX_EMERALDS             (DX + XX_EMERALDS)
142 #define DY_EMERALDS             (DY + YY_EMERALDS)
143 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
144 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
145 #define DX_KEYS                 (DX + XX_KEYS)
146 #define DY_KEYS                 (DY + YY_KEYS)
147 #define DX_SCORE                (DX + XX_SCORE)
148 #define DY_SCORE                (DY + YY_SCORE)
149 #define DX_TIME1                (DX + XX_TIME1)
150 #define DX_TIME2                (DX + XX_TIME2)
151 #define DX_TIME                 (DX + XX_TIME)
152 #define DY_TIME                 (DY + YY_TIME)
153
154 #if 1
155 /* game panel display and control definitions */
156
157 #define GAME_PANEL_LEVEL_NUMBER                 0
158 #define GAME_PANEL_GEMS                         1
159 #define GAME_PANEL_INVENTORY_COUNT              2
160 #define GAME_PANEL_INVENTORY_FIRST_1            3
161 #define GAME_PANEL_INVENTORY_FIRST_2            4
162 #define GAME_PANEL_INVENTORY_FIRST_3            5
163 #define GAME_PANEL_INVENTORY_FIRST_4            6
164 #define GAME_PANEL_INVENTORY_FIRST_5            7
165 #define GAME_PANEL_INVENTORY_FIRST_6            8
166 #define GAME_PANEL_INVENTORY_FIRST_7            9
167 #define GAME_PANEL_INVENTORY_FIRST_8            10
168 #define GAME_PANEL_INVENTORY_LAST_1             11
169 #define GAME_PANEL_INVENTORY_LAST_2             12
170 #define GAME_PANEL_INVENTORY_LAST_3             13
171 #define GAME_PANEL_INVENTORY_LAST_4             14
172 #define GAME_PANEL_INVENTORY_LAST_5             15
173 #define GAME_PANEL_INVENTORY_LAST_6             16
174 #define GAME_PANEL_INVENTORY_LAST_7             17
175 #define GAME_PANEL_INVENTORY_LAST_8             18
176 #define GAME_PANEL_KEY_1                        19
177 #define GAME_PANEL_KEY_2                        20
178 #define GAME_PANEL_KEY_3                        21
179 #define GAME_PANEL_KEY_4                        22
180 #define GAME_PANEL_KEY_5                        23
181 #define GAME_PANEL_KEY_6                        24
182 #define GAME_PANEL_KEY_7                        25
183 #define GAME_PANEL_KEY_8                        26
184 #define GAME_PANEL_KEY_WHITE                    27
185 #define GAME_PANEL_KEY_WHITE_COUNT              28
186 #define GAME_PANEL_SCORE                        29
187 #define GAME_PANEL_HIGHSCORE                    30
188 #define GAME_PANEL_TIME                         31
189 #define GAME_PANEL_TIME_HH                      32
190 #define GAME_PANEL_TIME_MM                      33
191 #define GAME_PANEL_TIME_SS                      34
192 #define GAME_PANEL_SHIELD_NORMAL                35
193 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
194 #define GAME_PANEL_SHIELD_DEADLY                37
195 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
196 #define GAME_PANEL_EXIT                         39
197 #define GAME_PANEL_EMC_MAGIC_BALL               40
198 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
199 #define GAME_PANEL_LIGHT_SWITCH                 42
200 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
201 #define GAME_PANEL_TIMEGATE_SWITCH              44
202 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
203 #define GAME_PANEL_SWITCHGATE_SWITCH            46
204 #define GAME_PANEL_EMC_LENSES                   47
205 #define GAME_PANEL_EMC_LENSES_TIME              48
206 #define GAME_PANEL_EMC_MAGNIFIER                49
207 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
208 #define GAME_PANEL_BALLOON_SWITCH               51
209 #define GAME_PANEL_DYNABOMB_NUMBER              52
210 #define GAME_PANEL_DYNABOMB_SIZE                53
211 #define GAME_PANEL_DYNABOMB_POWER               54
212 #define GAME_PANEL_PENGUINS                     55
213 #define GAME_PANEL_SOKOBAN_OBJECTS              56
214 #define GAME_PANEL_SOKOBAN_FIELDS               57
215 #define GAME_PANEL_ROBOT_WHEEL                  58
216 #define GAME_PANEL_CONVEYOR_BELT_1              59
217 #define GAME_PANEL_CONVEYOR_BELT_2              60
218 #define GAME_PANEL_CONVEYOR_BELT_3              61
219 #define GAME_PANEL_CONVEYOR_BELT_4              62
220 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
221 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
222 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
223 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
224 #define GAME_PANEL_MAGIC_WALL                   67
225 #define GAME_PANEL_MAGIC_WALL_TIME              68
226 #define GAME_PANEL_GRAVITY_STATE                69
227 #define GAME_PANEL_GRAPHIC_1                    70
228 #define GAME_PANEL_GRAPHIC_2                    71
229 #define GAME_PANEL_GRAPHIC_3                    72
230 #define GAME_PANEL_GRAPHIC_4                    73
231 #define GAME_PANEL_GRAPHIC_5                    74
232 #define GAME_PANEL_GRAPHIC_6                    75
233 #define GAME_PANEL_GRAPHIC_7                    76
234 #define GAME_PANEL_GRAPHIC_8                    77
235 #define GAME_PANEL_ELEMENT_1                    78
236 #define GAME_PANEL_ELEMENT_2                    79
237 #define GAME_PANEL_ELEMENT_3                    80
238 #define GAME_PANEL_ELEMENT_4                    81
239 #define GAME_PANEL_ELEMENT_5                    82
240 #define GAME_PANEL_ELEMENT_6                    83
241 #define GAME_PANEL_ELEMENT_7                    84
242 #define GAME_PANEL_ELEMENT_8                    85
243 #define GAME_PANEL_ELEMENT_COUNT_1              86
244 #define GAME_PANEL_ELEMENT_COUNT_2              87
245 #define GAME_PANEL_ELEMENT_COUNT_3              88
246 #define GAME_PANEL_ELEMENT_COUNT_4              89
247 #define GAME_PANEL_ELEMENT_COUNT_5              90
248 #define GAME_PANEL_ELEMENT_COUNT_6              91
249 #define GAME_PANEL_ELEMENT_COUNT_7              92
250 #define GAME_PANEL_ELEMENT_COUNT_8              93
251 #define GAME_PANEL_CE_SCORE_1                   94
252 #define GAME_PANEL_CE_SCORE_2                   95
253 #define GAME_PANEL_CE_SCORE_3                   96
254 #define GAME_PANEL_CE_SCORE_4                   97
255 #define GAME_PANEL_CE_SCORE_5                   98
256 #define GAME_PANEL_CE_SCORE_6                   99
257 #define GAME_PANEL_CE_SCORE_7                   100
258 #define GAME_PANEL_CE_SCORE_8                   101
259 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
260 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
261 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
262 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
263 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
264 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
265 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
266 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
267 #define GAME_PANEL_PLAYER_NAME                  110
268 #define GAME_PANEL_LEVEL_NAME                   111
269 #define GAME_PANEL_LEVEL_AUTHOR                 112
270
271 #define NUM_GAME_PANEL_CONTROLS                 113
272
273 struct GamePanelOrderInfo
274 {
275   int nr;
276   int sort_priority;
277 };
278
279 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
280
281 struct GamePanelControlInfo
282 {
283   int nr;
284
285   struct TextPosInfo *pos;
286   int type;
287
288   int value, last_value;
289   int frame, last_frame;
290   int gfx_frame;
291   int gfx_random;
292 };
293
294 static struct GamePanelControlInfo game_panel_controls[] =
295 {
296   {
297     GAME_PANEL_LEVEL_NUMBER,
298     &game.panel.level_number,
299     TYPE_INTEGER,
300   },
301   {
302     GAME_PANEL_GEMS,
303     &game.panel.gems,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_INVENTORY_COUNT,
308     &game.panel.inventory_count,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_FIRST_1,
313     &game.panel.inventory_first[0],
314     TYPE_ELEMENT,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_2,
318     &game.panel.inventory_first[1],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_3,
323     &game.panel.inventory_first[2],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_4,
328     &game.panel.inventory_first[3],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_5,
333     &game.panel.inventory_first[4],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_6,
338     &game.panel.inventory_first[5],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_7,
343     &game.panel.inventory_first[6],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_8,
348     &game.panel.inventory_first[7],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_LAST_1,
353     &game.panel.inventory_last[0],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_2,
358     &game.panel.inventory_last[1],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_3,
363     &game.panel.inventory_last[2],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_4,
368     &game.panel.inventory_last[3],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_5,
373     &game.panel.inventory_last[4],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_6,
378     &game.panel.inventory_last[5],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_7,
383     &game.panel.inventory_last[6],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_8,
388     &game.panel.inventory_last[7],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_KEY_1,
393     &game.panel.key[0],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_2,
398     &game.panel.key[1],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_3,
403     &game.panel.key[2],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_4,
408     &game.panel.key[3],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_5,
413     &game.panel.key[4],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_6,
418     &game.panel.key[5],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_7,
423     &game.panel.key[6],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_8,
428     &game.panel.key[7],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_WHITE,
433     &game.panel.key_white,
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE_COUNT,
438     &game.panel.key_white_count,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_SCORE,
443     &game.panel.score,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_HIGHSCORE,
448     &game.panel.highscore,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_TIME,
453     &game.panel.time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME_HH,
458     &game.panel.time_hh,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_MM,
463     &game.panel.time_mm,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_SS,
468     &game.panel.time_ss,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_SHIELD_NORMAL,
473     &game.panel.shield_normal,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL_TIME,
478     &game.panel.shield_normal_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_SHIELD_DEADLY,
483     &game.panel.shield_deadly,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY_TIME,
488     &game.panel.shield_deadly_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_EXIT,
493     &game.panel.exit,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_MAGIC_BALL,
498     &game.panel.emc_magic_ball,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
503     &game.panel.emc_magic_ball_switch,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_LIGHT_SWITCH,
508     &game.panel.light_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH_TIME,
513     &game.panel.light_switch_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_TIMEGATE_SWITCH,
518     &game.panel.timegate_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH_TIME,
523     &game.panel.timegate_switch_time,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_SWITCHGATE_SWITCH,
528     &game.panel.switchgate_switch,
529     TYPE_ELEMENT,
530   },
531   {
532     GAME_PANEL_EMC_LENSES,
533     &game.panel.emc_lenses,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES_TIME,
538     &game.panel.emc_lenses_time,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_EMC_MAGNIFIER,
543     &game.panel.emc_magnifier,
544     TYPE_ELEMENT,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER_TIME,
548     &game.panel.emc_magnifier_time,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_BALLOON_SWITCH,
553     &game.panel.balloon_switch,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_DYNABOMB_NUMBER,
558     &game.panel.dynabomb_number,
559     TYPE_INTEGER,
560   },
561   {
562     GAME_PANEL_DYNABOMB_SIZE,
563     &game.panel.dynabomb_size,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_POWER,
568     &game.panel.dynabomb_power,
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_PENGUINS,
573     &game.panel.penguins,
574     TYPE_INTEGER,
575   },
576   {
577     GAME_PANEL_SOKOBAN_OBJECTS,
578     &game.panel.sokoban_objects,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_FIELDS,
583     &game.panel.sokoban_fields,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_ROBOT_WHEEL,
588     &game.panel.robot_wheel,
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_1,
593     &game.panel.conveyor_belt[0],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_2,
598     &game.panel.conveyor_belt[1],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_3,
603     &game.panel.conveyor_belt[2],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_4,
608     &game.panel.conveyor_belt[3],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
613     &game.panel.conveyor_belt_switch[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
618     &game.panel.conveyor_belt_switch[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
623     &game.panel.conveyor_belt_switch[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
628     &game.panel.conveyor_belt_switch[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_MAGIC_WALL,
633     &game.panel.magic_wall,
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL_TIME,
638     &game.panel.magic_wall_time,
639     TYPE_INTEGER,
640   },
641   {
642     GAME_PANEL_GRAVITY_STATE,
643     &game.panel.gravity_state,
644     TYPE_STRING,
645   },
646   {
647     GAME_PANEL_GRAPHIC_1,
648     &game.panel.graphic[0],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_GRAPHIC_2,
653     &game.panel.graphic[1],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_3,
658     &game.panel.graphic[2],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_4,
663     &game.panel.graphic[3],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_5,
668     &game.panel.graphic[4],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_6,
673     &game.panel.graphic[5],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_7,
678     &game.panel.graphic[6],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_8,
683     &game.panel.graphic[7],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_1,
688     &game.panel.element[0],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_2,
693     &game.panel.element[1],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_3,
698     &game.panel.element[2],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_4,
703     &game.panel.element[3],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_5,
708     &game.panel.element[4],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_6,
713     &game.panel.element[5],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_7,
718     &game.panel.element[6],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_8,
723     &game.panel.element[7],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_1,
728     &game.panel.element_count[0],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_2,
733     &game.panel.element_count[1],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_3,
738     &game.panel.element_count[2],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_4,
743     &game.panel.element_count[3],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_5,
748     &game.panel.element_count[4],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_6,
753     &game.panel.element_count[5],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_7,
758     &game.panel.element_count[6],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_8,
763     &game.panel.element_count[7],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_1,
768     &game.panel.ce_score[0],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_2,
773     &game.panel.ce_score[1],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_3,
778     &game.panel.ce_score[2],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_4,
783     &game.panel.ce_score[3],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_5,
788     &game.panel.ce_score[4],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_6,
793     &game.panel.ce_score[5],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_7,
798     &game.panel.ce_score[6],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_8,
803     &game.panel.ce_score[7],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_1_ELEMENT,
808     &game.panel.ce_score_element[0],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_CE_SCORE_2_ELEMENT,
813     &game.panel.ce_score_element[1],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_3_ELEMENT,
818     &game.panel.ce_score_element[2],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_4_ELEMENT,
823     &game.panel.ce_score_element[3],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_5_ELEMENT,
828     &game.panel.ce_score_element[4],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_6_ELEMENT,
833     &game.panel.ce_score_element[5],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_7_ELEMENT,
838     &game.panel.ce_score_element[6],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_8_ELEMENT,
843     &game.panel.ce_score_element[7],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_PLAYER_NAME,
848     &game.panel.player_name,
849     TYPE_STRING,
850   },
851   {
852     GAME_PANEL_LEVEL_NAME,
853     &game.panel.level_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_AUTHOR,
858     &game.panel.level_author,
859     TYPE_STRING,
860   },
861
862   {
863     -1,
864     NULL,
865     -1,
866   }
867 };
868 #endif
869
870
871 /* values for delayed check of falling and moving elements and for collision */
872 #define CHECK_DELAY_MOVING      3
873 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
874 #define CHECK_DELAY_COLLISION   2
875 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
876
877 /* values for initial player move delay (initial delay counter value) */
878 #define INITIAL_MOVE_DELAY_OFF  -1
879 #define INITIAL_MOVE_DELAY_ON   0
880
881 /* values for player movement speed (which is in fact a delay value) */
882 #define MOVE_DELAY_MIN_SPEED    32
883 #define MOVE_DELAY_NORMAL_SPEED 8
884 #define MOVE_DELAY_HIGH_SPEED   4
885 #define MOVE_DELAY_MAX_SPEED    1
886
887 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
888 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
889
890 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
891 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
892
893 /* values for other actions */
894 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
895 #define MOVE_STEPSIZE_MIN       (1)
896 #define MOVE_STEPSIZE_MAX       (TILEX)
897
898 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
899 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
900
901 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
902
903 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
904                                  RND(element_info[e].push_delay_random))
905 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
906                                  RND(element_info[e].drop_delay_random))
907 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
908                                  RND(element_info[e].move_delay_random))
909 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
910                                     (element_info[e].move_delay_random))
911 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
912                                  RND(element_info[e].ce_value_random_initial))
913 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
914 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
915                                  RND((c)->delay_random * (c)->delay_frames))
916 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
917                                  RND((c)->delay_random))
918
919
920 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
921          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
922
923 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
924         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
925          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
926          (be) + (e) - EL_SELF)
927
928 #define GET_PLAYER_FROM_BITS(p)                                         \
929         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
930
931 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
932         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
933          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
934          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
935          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
936          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
937          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
938          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
939          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
940          (e))
941
942 #define CAN_GROW_INTO(e)                                                \
943         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
944
945 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
946                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
947                                         (condition)))
948
949 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
950                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
951                                         (CAN_MOVE_INTO_ACID(e) &&       \
952                                          Feld[x][y] == EL_ACID) ||      \
953                                         (condition)))
954
955 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
956                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Feld[x][y] == EL_ACID) ||      \
959                                         (condition)))
960
961 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
962                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
963                                         (condition) ||                  \
964                                         (CAN_MOVE_INTO_ACID(e) &&       \
965                                          Feld[x][y] == EL_ACID) ||      \
966                                         (DONT_COLLIDE_WITH(e) &&        \
967                                          IS_PLAYER(x, y) &&             \
968                                          !PLAYER_ENEMY_PROTECTED(x, y))))
969
970 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
971         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
972
973 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
975
976 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
978
979 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
980         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
981                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
982
983 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
984         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
985
986 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
987         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
988
989 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
990         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
991
992 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
993         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
994
995 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
996         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
997
998 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
999         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1000                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1001                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1002                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1003                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1004 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1005         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1006
1007 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1008         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1009
1010 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1011         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012
1013 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1014         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1015                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1016
1017 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1018
1019 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1020                 (!IS_PLAYER(x, y) &&                                    \
1021                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1022
1023 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1024         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1025
1026 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1027 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1028
1029 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1030 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1031 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1032 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1033
1034 /* game button identifiers */
1035 #define GAME_CTRL_ID_STOP               0
1036 #define GAME_CTRL_ID_PAUSE              1
1037 #define GAME_CTRL_ID_PLAY               2
1038 #define SOUND_CTRL_ID_MUSIC             3
1039 #define SOUND_CTRL_ID_LOOPS             4
1040 #define SOUND_CTRL_ID_SIMPLE            5
1041
1042 #define NUM_GAME_BUTTONS                6
1043
1044
1045 /* forward declaration for internal use */
1046
1047 static void CreateField(int, int, int);
1048
1049 static void ResetGfxAnimation(int, int);
1050
1051 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1052 static void AdvanceFrameAndPlayerCounters(int);
1053
1054 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1055 static boolean MovePlayer(struct PlayerInfo *, int, int);
1056 static void ScrollPlayer(struct PlayerInfo *, int);
1057 static void ScrollScreen(struct PlayerInfo *, int);
1058
1059 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1060 static boolean DigFieldByCE(int, int, int);
1061 static boolean SnapField(struct PlayerInfo *, int, int);
1062 static boolean DropElement(struct PlayerInfo *);
1063
1064 static void InitBeltMovement(void);
1065 static void CloseAllOpenTimegates(void);
1066 static void CheckGravityMovement(struct PlayerInfo *);
1067 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1068 static void KillPlayerUnlessEnemyProtected(int, int);
1069 static void KillPlayerUnlessExplosionProtected(int, int);
1070
1071 static void TestIfPlayerTouchesCustomElement(int, int);
1072 static void TestIfElementTouchesCustomElement(int, int);
1073 static void TestIfElementHitsCustomElement(int, int, int);
1074 #if 0
1075 static void TestIfElementSmashesCustomElement(int, int, int);
1076 #endif
1077
1078 static void HandleElementChange(int, int, int);
1079 static void ExecuteCustomElementAction(int, int, int, int);
1080 static boolean ChangeElement(int, int, int, int);
1081
1082 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1083 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1084         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1085 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1086         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1087 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1088         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1089 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1090         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1091
1092 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1093 #define CheckElementChange(x, y, e, te, ev)                             \
1094         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1095 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1096         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1097 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1098         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1099
1100 static void PlayLevelSound(int, int, int);
1101 static void PlayLevelSoundNearest(int, int, int);
1102 static void PlayLevelSoundAction(int, int, int);
1103 static void PlayLevelSoundElementAction(int, int, int, int);
1104 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1105 static void PlayLevelSoundActionIfLoop(int, int, int);
1106 static void StopLevelSoundActionIfLoop(int, int, int);
1107 static void PlayLevelMusic();
1108
1109 static void MapGameButtons();
1110 static void HandleGameButtons(struct GadgetInfo *);
1111
1112 int AmoebeNachbarNr(int, int);
1113 void AmoebeUmwandeln(int, int);
1114 void ContinueMoving(int, int);
1115 void Bang(int, int);
1116 void InitMovDir(int, int);
1117 void InitAmoebaNr(int, int);
1118 int NewHiScore(void);
1119
1120 void TestIfGoodThingHitsBadThing(int, int, int);
1121 void TestIfBadThingHitsGoodThing(int, int, int);
1122 void TestIfPlayerTouchesBadThing(int, int);
1123 void TestIfPlayerRunsIntoBadThing(int, int, int);
1124 void TestIfBadThingTouchesPlayer(int, int);
1125 void TestIfBadThingRunsIntoPlayer(int, int, int);
1126 void TestIfFriendTouchesBadThing(int, int);
1127 void TestIfBadThingTouchesFriend(int, int);
1128 void TestIfBadThingTouchesOtherBadThing(int, int);
1129 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1130
1131 void KillPlayer(struct PlayerInfo *);
1132 void BuryPlayer(struct PlayerInfo *);
1133 void RemovePlayer(struct PlayerInfo *);
1134
1135 static int getInvisibleActiveFromInvisibleElement(int);
1136 static int getInvisibleFromInvisibleActiveElement(int);
1137
1138 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1139
1140 /* for detection of endless loops, caused by custom element programming */
1141 /* (using maximal playfield width x 10 is just a rough approximation) */
1142 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1143
1144 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1145 {                                                                       \
1146   if (recursion_loop_detected)                                          \
1147     return (rc);                                                        \
1148                                                                         \
1149   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1150   {                                                                     \
1151     recursion_loop_detected = TRUE;                                     \
1152     recursion_loop_element = (e);                                       \
1153   }                                                                     \
1154                                                                         \
1155   recursion_loop_depth++;                                               \
1156 }
1157
1158 #define RECURSION_LOOP_DETECTION_END()                                  \
1159 {                                                                       \
1160   recursion_loop_depth--;                                               \
1161 }
1162
1163 static int recursion_loop_depth;
1164 static boolean recursion_loop_detected;
1165 static boolean recursion_loop_element;
1166
1167
1168 /* ------------------------------------------------------------------------- */
1169 /* definition of elements that automatically change to other elements after  */
1170 /* a specified time, eventually calling a function when changing             */
1171 /* ------------------------------------------------------------------------- */
1172
1173 /* forward declaration for changer functions */
1174 static void InitBuggyBase(int, int);
1175 static void WarnBuggyBase(int, int);
1176
1177 static void InitTrap(int, int);
1178 static void ActivateTrap(int, int);
1179 static void ChangeActiveTrap(int, int);
1180
1181 static void InitRobotWheel(int, int);
1182 static void RunRobotWheel(int, int);
1183 static void StopRobotWheel(int, int);
1184
1185 static void InitTimegateWheel(int, int);
1186 static void RunTimegateWheel(int, int);
1187
1188 static void InitMagicBallDelay(int, int);
1189 static void ActivateMagicBall(int, int);
1190
1191 struct ChangingElementInfo
1192 {
1193   int element;
1194   int target_element;
1195   int change_delay;
1196   void (*pre_change_function)(int x, int y);
1197   void (*change_function)(int x, int y);
1198   void (*post_change_function)(int x, int y);
1199 };
1200
1201 static struct ChangingElementInfo change_delay_list[] =
1202 {
1203   {
1204     EL_NUT_BREAKING,
1205     EL_EMERALD,
1206     6,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_PEARL_BREAKING,
1213     EL_EMPTY,
1214     8,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_EXIT_OPENING,
1221     EL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_EXIT_CLOSING,
1229     EL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_STEEL_EXIT_OPENING,
1237     EL_STEEL_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_STEEL_EXIT_CLOSING,
1245     EL_STEEL_EXIT_CLOSED,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_EXIT_OPENING,
1253     EL_EM_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_EXIT_CLOSING,
1261 #if 1
1262     EL_EMPTY,
1263 #else
1264     EL_EM_EXIT_CLOSED,
1265 #endif
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_OPENING,
1273     EL_EM_STEEL_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_STEEL_EXIT_CLOSING,
1281 #if 1
1282     EL_STEELWALL,
1283 #else
1284     EL_EM_STEEL_EXIT_CLOSED,
1285 #endif
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_EXIT_OPENING,
1293     EL_SP_EXIT_OPEN,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SP_EXIT_CLOSING,
1301     EL_SP_EXIT_CLOSED,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_SWITCHGATE_OPENING,
1309     EL_SWITCHGATE_OPEN,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_SWITCHGATE_CLOSING,
1317     EL_SWITCHGATE_CLOSED,
1318     29,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_TIMEGATE_OPENING,
1325     EL_TIMEGATE_OPEN,
1326     29,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_TIMEGATE_CLOSING,
1333     EL_TIMEGATE_CLOSED,
1334     29,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339
1340   {
1341     EL_ACID_SPLASH_LEFT,
1342     EL_EMPTY,
1343     8,
1344     NULL,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_ACID_SPLASH_RIGHT,
1350     EL_EMPTY,
1351     8,
1352     NULL,
1353     NULL,
1354     NULL
1355   },
1356   {
1357     EL_SP_BUGGY_BASE,
1358     EL_SP_BUGGY_BASE_ACTIVATING,
1359     0,
1360     InitBuggyBase,
1361     NULL,
1362     NULL
1363   },
1364   {
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     EL_SP_BUGGY_BASE_ACTIVE,
1367     0,
1368     InitBuggyBase,
1369     NULL,
1370     NULL
1371   },
1372   {
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     EL_SP_BUGGY_BASE,
1375     0,
1376     InitBuggyBase,
1377     WarnBuggyBase,
1378     NULL
1379   },
1380   {
1381     EL_TRAP,
1382     EL_TRAP_ACTIVE,
1383     0,
1384     InitTrap,
1385     NULL,
1386     ActivateTrap
1387   },
1388   {
1389     EL_TRAP_ACTIVE,
1390     EL_TRAP,
1391     31,
1392     NULL,
1393     ChangeActiveTrap,
1394     NULL
1395   },
1396   {
1397     EL_ROBOT_WHEEL_ACTIVE,
1398     EL_ROBOT_WHEEL,
1399     0,
1400     InitRobotWheel,
1401     RunRobotWheel,
1402     StopRobotWheel
1403   },
1404   {
1405     EL_TIMEGATE_SWITCH_ACTIVE,
1406     EL_TIMEGATE_SWITCH,
1407     0,
1408     InitTimegateWheel,
1409     RunTimegateWheel,
1410     NULL
1411   },
1412   {
1413     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1414     EL_DC_TIMEGATE_SWITCH,
1415     0,
1416     InitTimegateWheel,
1417     RunTimegateWheel,
1418     NULL
1419   },
1420   {
1421     EL_EMC_MAGIC_BALL_ACTIVE,
1422     EL_EMC_MAGIC_BALL_ACTIVE,
1423     0,
1424     InitMagicBallDelay,
1425     NULL,
1426     ActivateMagicBall
1427   },
1428   {
1429     EL_EMC_SPRING_BUMPER_ACTIVE,
1430     EL_EMC_SPRING_BUMPER,
1431     8,
1432     NULL,
1433     NULL,
1434     NULL
1435   },
1436   {
1437     EL_DIAGONAL_SHRINKING,
1438     EL_UNDEFINED,
1439     0,
1440     NULL,
1441     NULL,
1442     NULL
1443   },
1444   {
1445     EL_DIAGONAL_GROWING,
1446     EL_UNDEFINED,
1447     0,
1448     NULL,
1449     NULL,
1450     NULL,
1451   },
1452
1453   {
1454     EL_UNDEFINED,
1455     EL_UNDEFINED,
1456     -1,
1457     NULL,
1458     NULL,
1459     NULL
1460   }
1461 };
1462
1463 struct
1464 {
1465   int element;
1466   int push_delay_fixed, push_delay_random;
1467 }
1468 push_delay_list[] =
1469 {
1470   { EL_SPRING,                  0, 0 },
1471   { EL_BALLOON,                 0, 0 },
1472
1473   { EL_SOKOBAN_OBJECT,          2, 0 },
1474   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1475   { EL_SATELLITE,               2, 0 },
1476   { EL_SP_DISK_YELLOW,          2, 0 },
1477
1478   { EL_UNDEFINED,               0, 0 },
1479 };
1480
1481 struct
1482 {
1483   int element;
1484   int move_stepsize;
1485 }
1486 move_stepsize_list[] =
1487 {
1488   { EL_AMOEBA_DROP,             2 },
1489   { EL_AMOEBA_DROPPING,         2 },
1490   { EL_QUICKSAND_FILLING,       1 },
1491   { EL_QUICKSAND_EMPTYING,      1 },
1492   { EL_QUICKSAND_FAST_FILLING,  2 },
1493   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1494   { EL_MAGIC_WALL_FILLING,      2 },
1495   { EL_MAGIC_WALL_EMPTYING,     2 },
1496   { EL_BD_MAGIC_WALL_FILLING,   2 },
1497   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1498   { EL_DC_MAGIC_WALL_FILLING,   2 },
1499   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1500
1501   { EL_UNDEFINED,               0 },
1502 };
1503
1504 struct
1505 {
1506   int element;
1507   int count;
1508 }
1509 collect_count_list[] =
1510 {
1511   { EL_EMERALD,                 1 },
1512   { EL_BD_DIAMOND,              1 },
1513   { EL_EMERALD_YELLOW,          1 },
1514   { EL_EMERALD_RED,             1 },
1515   { EL_EMERALD_PURPLE,          1 },
1516   { EL_DIAMOND,                 3 },
1517   { EL_SP_INFOTRON,             1 },
1518   { EL_PEARL,                   5 },
1519   { EL_CRYSTAL,                 8 },
1520
1521   { EL_UNDEFINED,               0 },
1522 };
1523
1524 struct
1525 {
1526   int element;
1527   int direction;
1528 }
1529 access_direction_list[] =
1530 {
1531   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1532   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1533   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1534   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1535   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1536   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1537   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1538   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1539   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1540   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1541   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1542
1543   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1544   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1545   { EL_SP_PORT_UP,                                                   MV_DOWN },
1546   { EL_SP_PORT_DOWN,                                         MV_UP           },
1547   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1548   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1549   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1550   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1551   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1552   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1553   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1554   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1555   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1556   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1557   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1558   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1559   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1560   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1561   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1562
1563   { EL_UNDEFINED,                       MV_NONE                              }
1564 };
1565
1566 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1567
1568 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1569 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1570 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1571                                  IS_JUST_CHANGING(x, y))
1572
1573 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1574
1575 /* static variables for playfield scan mode (scanning forward or backward) */
1576 static int playfield_scan_start_x = 0;
1577 static int playfield_scan_start_y = 0;
1578 static int playfield_scan_delta_x = 1;
1579 static int playfield_scan_delta_y = 1;
1580
1581 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1582                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1583                                      (y) += playfield_scan_delta_y)     \
1584                                 for ((x) = playfield_scan_start_x;      \
1585                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1586                                      (x) += playfield_scan_delta_x)
1587
1588 #ifdef DEBUG
1589 void DEBUG_SetMaximumDynamite()
1590 {
1591   int i;
1592
1593   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1594     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1595       local_player->inventory_element[local_player->inventory_size++] =
1596         EL_DYNAMITE;
1597 }
1598 #endif
1599
1600 static void InitPlayfieldScanModeVars()
1601 {
1602   if (game.use_reverse_scan_direction)
1603   {
1604     playfield_scan_start_x = lev_fieldx - 1;
1605     playfield_scan_start_y = lev_fieldy - 1;
1606
1607     playfield_scan_delta_x = -1;
1608     playfield_scan_delta_y = -1;
1609   }
1610   else
1611   {
1612     playfield_scan_start_x = 0;
1613     playfield_scan_start_y = 0;
1614
1615     playfield_scan_delta_x = 1;
1616     playfield_scan_delta_y = 1;
1617   }
1618 }
1619
1620 static void InitPlayfieldScanMode(int mode)
1621 {
1622   game.use_reverse_scan_direction =
1623     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1624
1625   InitPlayfieldScanModeVars();
1626 }
1627
1628 static int get_move_delay_from_stepsize(int move_stepsize)
1629 {
1630   move_stepsize =
1631     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1632
1633   /* make sure that stepsize value is always a power of 2 */
1634   move_stepsize = (1 << log_2(move_stepsize));
1635
1636   return TILEX / move_stepsize;
1637 }
1638
1639 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1640                                boolean init_game)
1641 {
1642   int player_nr = player->index_nr;
1643   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1644   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1645
1646   /* do no immediately change move delay -- the player might just be moving */
1647   player->move_delay_value_next = move_delay;
1648
1649   /* information if player can move must be set separately */
1650   player->cannot_move = cannot_move;
1651
1652   if (init_game)
1653   {
1654     player->move_delay       = game.initial_move_delay[player_nr];
1655     player->move_delay_value = game.initial_move_delay_value[player_nr];
1656
1657     player->move_delay_value_next = -1;
1658
1659     player->move_delay_reset_counter = 0;
1660   }
1661 }
1662
1663 void GetPlayerConfig()
1664 {
1665   GameFrameDelay = setup.game_frame_delay;
1666
1667   if (!audio.sound_available)
1668     setup.sound_simple = FALSE;
1669
1670   if (!audio.loops_available)
1671     setup.sound_loops = FALSE;
1672
1673   if (!audio.music_available)
1674     setup.sound_music = FALSE;
1675
1676   if (!video.fullscreen_available)
1677     setup.fullscreen = FALSE;
1678
1679   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1680
1681   SetAudioMode(setup.sound);
1682   InitJoysticks();
1683 }
1684
1685 int GetElementFromGroupElement(int element)
1686 {
1687   if (IS_GROUP_ELEMENT(element))
1688   {
1689     struct ElementGroupInfo *group = element_info[element].group;
1690     int last_anim_random_frame = gfx.anim_random_frame;
1691     int element_pos;
1692
1693     if (group->choice_mode == ANIM_RANDOM)
1694       gfx.anim_random_frame = RND(group->num_elements_resolved);
1695
1696     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1697                                     group->choice_mode, 0,
1698                                     group->choice_pos);
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = last_anim_random_frame;
1702
1703     group->choice_pos++;
1704
1705     element = group->element_resolved[element_pos];
1706   }
1707
1708   return element;
1709 }
1710
1711 static void InitPlayerField(int x, int y, int element, boolean init_game)
1712 {
1713   if (element == EL_SP_MURPHY)
1714   {
1715     if (init_game)
1716     {
1717       if (stored_player[0].present)
1718       {
1719         Feld[x][y] = EL_SP_MURPHY_CLONE;
1720
1721         return;
1722       }
1723       else
1724       {
1725         stored_player[0].initial_element = element;
1726         stored_player[0].use_murphy = TRUE;
1727
1728         if (!level.use_artwork_element[0])
1729           stored_player[0].artwork_element = EL_SP_MURPHY;
1730       }
1731
1732       Feld[x][y] = EL_PLAYER_1;
1733     }
1734   }
1735
1736   if (init_game)
1737   {
1738     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1739     int jx = player->jx, jy = player->jy;
1740
1741     player->present = TRUE;
1742
1743     player->block_last_field = (element == EL_SP_MURPHY ?
1744                                 level.sp_block_last_field :
1745                                 level.block_last_field);
1746
1747     /* ---------- initialize player's last field block delay --------------- */
1748
1749     /* always start with reliable default value (no adjustment needed) */
1750     player->block_delay_adjustment = 0;
1751
1752     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1753     if (player->block_last_field && element == EL_SP_MURPHY)
1754       player->block_delay_adjustment = 1;
1755
1756     /* special case 2: in game engines before 3.1.1, blocking was different */
1757     if (game.use_block_last_field_bug)
1758       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1759
1760     if (!options.network || player->connected)
1761     {
1762       player->active = TRUE;
1763
1764       /* remove potentially duplicate players */
1765       if (StorePlayer[jx][jy] == Feld[x][y])
1766         StorePlayer[jx][jy] = 0;
1767
1768       StorePlayer[x][y] = Feld[x][y];
1769
1770       if (options.debug)
1771       {
1772         printf("Player %d activated.\n", player->element_nr);
1773         printf("[Local player is %d and currently %s.]\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778
1779     Feld[x][y] = EL_EMPTY;
1780
1781     player->jx = player->last_jx = x;
1782     player->jy = player->last_jy = y;
1783   }
1784 }
1785
1786 static void InitField(int x, int y, boolean init_game)
1787 {
1788   int element = Feld[x][y];
1789
1790   switch (element)
1791   {
1792     case EL_SP_MURPHY:
1793     case EL_PLAYER_1:
1794     case EL_PLAYER_2:
1795     case EL_PLAYER_3:
1796     case EL_PLAYER_4:
1797       InitPlayerField(x, y, element, init_game);
1798       break;
1799
1800     case EL_SOKOBAN_FIELD_PLAYER:
1801       element = Feld[x][y] = EL_PLAYER_1;
1802       InitField(x, y, init_game);
1803
1804       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1805       InitField(x, y, init_game);
1806       break;
1807
1808     case EL_SOKOBAN_FIELD_EMPTY:
1809       local_player->sokobanfields_still_needed++;
1810       break;
1811
1812     case EL_STONEBLOCK:
1813       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1814         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1815       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1816         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1817       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1818         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1819       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1820         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1821       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1822         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1823       break;
1824
1825     case EL_BUG:
1826     case EL_BUG_RIGHT:
1827     case EL_BUG_UP:
1828     case EL_BUG_LEFT:
1829     case EL_BUG_DOWN:
1830     case EL_SPACESHIP:
1831     case EL_SPACESHIP_RIGHT:
1832     case EL_SPACESHIP_UP:
1833     case EL_SPACESHIP_LEFT:
1834     case EL_SPACESHIP_DOWN:
1835     case EL_BD_BUTTERFLY:
1836     case EL_BD_BUTTERFLY_RIGHT:
1837     case EL_BD_BUTTERFLY_UP:
1838     case EL_BD_BUTTERFLY_LEFT:
1839     case EL_BD_BUTTERFLY_DOWN:
1840     case EL_BD_FIREFLY:
1841     case EL_BD_FIREFLY_RIGHT:
1842     case EL_BD_FIREFLY_UP:
1843     case EL_BD_FIREFLY_LEFT:
1844     case EL_BD_FIREFLY_DOWN:
1845     case EL_PACMAN_RIGHT:
1846     case EL_PACMAN_UP:
1847     case EL_PACMAN_LEFT:
1848     case EL_PACMAN_DOWN:
1849     case EL_YAMYAM:
1850     case EL_YAMYAM_LEFT:
1851     case EL_YAMYAM_RIGHT:
1852     case EL_YAMYAM_UP:
1853     case EL_YAMYAM_DOWN:
1854     case EL_DARK_YAMYAM:
1855     case EL_ROBOT:
1856     case EL_PACMAN:
1857     case EL_SP_SNIKSNAK:
1858     case EL_SP_ELECTRON:
1859     case EL_MOLE:
1860     case EL_MOLE_LEFT:
1861     case EL_MOLE_RIGHT:
1862     case EL_MOLE_UP:
1863     case EL_MOLE_DOWN:
1864       InitMovDir(x, y);
1865       break;
1866
1867     case EL_AMOEBA_FULL:
1868     case EL_BD_AMOEBA:
1869       InitAmoebaNr(x, y);
1870       break;
1871
1872     case EL_AMOEBA_DROP:
1873       if (y == lev_fieldy - 1)
1874       {
1875         Feld[x][y] = EL_AMOEBA_GROWING;
1876         Store[x][y] = EL_AMOEBA_WET;
1877       }
1878       break;
1879
1880     case EL_DYNAMITE_ACTIVE:
1881     case EL_SP_DISK_RED_ACTIVE:
1882     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1883     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1884     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1885     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1886       MovDelay[x][y] = 96;
1887       break;
1888
1889     case EL_EM_DYNAMITE_ACTIVE:
1890       MovDelay[x][y] = 32;
1891       break;
1892
1893     case EL_LAMP:
1894       local_player->lights_still_needed++;
1895       break;
1896
1897     case EL_PENGUIN:
1898       local_player->friends_still_needed++;
1899       break;
1900
1901     case EL_PIG:
1902     case EL_DRAGON:
1903       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1904       break;
1905
1906     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1907     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1908     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1909     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1910     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1911     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1912     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1913     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1914     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1915     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1916     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1917     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1918       if (init_game)
1919       {
1920         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1921         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1922         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1923
1924         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1925         {
1926           game.belt_dir[belt_nr] = belt_dir;
1927           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1928         }
1929         else    /* more than one switch -- set it like the first switch */
1930         {
1931           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1932         }
1933       }
1934       break;
1935
1936 #if !USE_BOTH_SWITCHGATE_SWITCHES
1937     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1938       if (init_game)
1939         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1940       break;
1941
1942     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1943       if (init_game)
1944         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1945       break;
1946 #endif
1947
1948     case EL_LIGHT_SWITCH_ACTIVE:
1949       if (init_game)
1950         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1951       break;
1952
1953     case EL_INVISIBLE_STEELWALL:
1954     case EL_INVISIBLE_WALL:
1955     case EL_INVISIBLE_SAND:
1956       if (game.light_time_left > 0 ||
1957           game.lenses_time_left > 0)
1958         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1959       break;
1960
1961     case EL_EMC_MAGIC_BALL:
1962       if (game.ball_state)
1963         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1964       break;
1965
1966     case EL_EMC_MAGIC_BALL_SWITCH:
1967       if (game.ball_state)
1968         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1969       break;
1970
1971     case EL_TRIGGER_PLAYER:
1972     case EL_TRIGGER_ELEMENT:
1973     case EL_TRIGGER_CE_VALUE:
1974     case EL_TRIGGER_CE_SCORE:
1975     case EL_SELF:
1976     case EL_ANY_ELEMENT:
1977     case EL_CURRENT_CE_VALUE:
1978     case EL_CURRENT_CE_SCORE:
1979     case EL_PREV_CE_1:
1980     case EL_PREV_CE_2:
1981     case EL_PREV_CE_3:
1982     case EL_PREV_CE_4:
1983     case EL_PREV_CE_5:
1984     case EL_PREV_CE_6:
1985     case EL_PREV_CE_7:
1986     case EL_PREV_CE_8:
1987     case EL_NEXT_CE_1:
1988     case EL_NEXT_CE_2:
1989     case EL_NEXT_CE_3:
1990     case EL_NEXT_CE_4:
1991     case EL_NEXT_CE_5:
1992     case EL_NEXT_CE_6:
1993     case EL_NEXT_CE_7:
1994     case EL_NEXT_CE_8:
1995       /* reference elements should not be used on the playfield */
1996       Feld[x][y] = EL_EMPTY;
1997       break;
1998
1999     default:
2000       if (IS_CUSTOM_ELEMENT(element))
2001       {
2002         if (CAN_MOVE(element))
2003           InitMovDir(x, y);
2004
2005 #if USE_NEW_CUSTOM_VALUE
2006         if (!element_info[element].use_last_ce_value || init_game)
2007           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2008 #endif
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   /* not needed to call InitMovDir() -- already done by InitField()! */
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   /* not needed to call InitMovDir() -- already done by InitField()! */
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 #if 1
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   /* pos >= 0: get element from bottom of the stack;
2080      pos <  0: get element from top of the stack */
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 void InitGameControlValues()
2127 {
2128   int i;
2129
2130   for (i = 0; game_panel_controls[i].nr != -1; i++)
2131   {
2132     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2133     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2134     struct TextPosInfo *pos = gpc->pos;
2135     int nr = gpc->nr;
2136     int type = gpc->type;
2137
2138     if (nr != i)
2139     {
2140       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2141       Error(ERR_EXIT, "this should not happen -- please debug");
2142     }
2143
2144     /* force update of game controls after initialization */
2145     gpc->value = gpc->last_value = -1;
2146     gpc->frame = gpc->last_frame = -1;
2147     gpc->gfx_frame = -1;
2148
2149     /* determine panel value width for later calculation of alignment */
2150     if (type == TYPE_INTEGER || type == TYPE_STRING)
2151     {
2152       pos->width = pos->size * getFontWidth(pos->font);
2153       pos->height = getFontHeight(pos->font);
2154     }
2155     else if (type == TYPE_ELEMENT)
2156     {
2157       pos->width = pos->size;
2158       pos->height = pos->size;
2159     }
2160
2161     /* fill structure for game panel draw order */
2162     gpo->nr = gpc->nr;
2163     gpo->sort_priority = pos->sort_priority;
2164   }
2165
2166   /* sort game panel controls according to sort_priority and control number */
2167   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2168         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2169 }
2170
2171 void UpdatePlayfieldElementCount()
2172 {
2173   boolean use_element_count = FALSE;
2174   int i, j, x, y;
2175
2176   /* first check if it is needed at all to calculate playfield element count */
2177   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2178     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2179       use_element_count = TRUE;
2180
2181   if (!use_element_count)
2182     return;
2183
2184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2185     element_info[i].element_count = 0;
2186
2187   SCAN_PLAYFIELD(x, y)
2188   {
2189     element_info[Feld[x][y]].element_count++;
2190   }
2191
2192   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2193     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2194       if (IS_IN_GROUP(j, i))
2195         element_info[EL_GROUP_START + i].element_count +=
2196           element_info[j].element_count;
2197 }
2198
2199 void UpdateGameControlValues()
2200 {
2201   int i, k;
2202   int time = (local_player->LevelSolved ?
2203               local_player->LevelSolved_CountingTime :
2204               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205               level.native_em_level->lev->time :
2206               level.time == 0 ? TimePlayed : TimeLeft);
2207   int score = (local_player->LevelSolved ?
2208                local_player->LevelSolved_CountingScore :
2209                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210                level.native_em_level->lev->score :
2211                local_player->score);
2212   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->required :
2214               local_player->gems_still_needed);
2215   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216                      level.native_em_level->lev->required > 0 :
2217                      local_player->gems_still_needed > 0 ||
2218                      local_player->sokobanfields_still_needed > 0 ||
2219                      local_player->lights_still_needed > 0);
2220
2221   UpdatePlayfieldElementCount();
2222
2223   /* update game panel control values */
2224
2225   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2226   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2227
2228   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2229   for (i = 0; i < MAX_NUM_KEYS; i++)
2230     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2231   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2233
2234   if (game.centered_player_nr == -1)
2235   {
2236     for (i = 0; i < MAX_PLAYERS; i++)
2237     {
2238       for (k = 0; k < MAX_NUM_KEYS; k++)
2239       {
2240         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241         {
2242           if (level.native_em_level->ply[i]->keys & (1 << k))
2243             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2244               get_key_element_from_nr(k);
2245         }
2246         else if (stored_player[i].key[k])
2247           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2248             get_key_element_from_nr(k);
2249       }
2250
2251       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           level.native_em_level->ply[i]->dynamite;
2254       else
2255         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256           stored_player[i].inventory_size;
2257
2258       if (stored_player[i].num_white_keys > 0)
2259         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2260           EL_DC_KEY_WHITE;
2261
2262       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2263         stored_player[i].num_white_keys;
2264     }
2265   }
2266   else
2267   {
2268     int player_nr = game.centered_player_nr;
2269
2270     for (k = 0; k < MAX_NUM_KEYS; k++)
2271     {
2272       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2273       {
2274         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2275           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2276             get_key_element_from_nr(k);
2277       }
2278       else if (stored_player[player_nr].key[k])
2279         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280           get_key_element_from_nr(k);
2281     }
2282
2283     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285         level.native_em_level->ply[player_nr]->dynamite;
2286     else
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         stored_player[player_nr].inventory_size;
2289
2290     if (stored_player[player_nr].num_white_keys > 0)
2291       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292
2293     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294       stored_player[player_nr].num_white_keys;
2295   }
2296
2297   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298   {
2299     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, i);
2301     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302       get_inventory_element_from_pos(local_player, -i - 1);
2303   }
2304
2305   game_panel_controls[GAME_PANEL_SCORE].value = score;
2306   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307
2308   game_panel_controls[GAME_PANEL_TIME].value = time;
2309
2310   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313
2314   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2315     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2316      EL_EMPTY);
2317   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2318     local_player->shield_normal_time_left;
2319   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2320     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2321      EL_EMPTY);
2322   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2323     local_player->shield_deadly_time_left;
2324
2325   game_panel_controls[GAME_PANEL_EXIT].value =
2326     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2327
2328   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2329     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2330   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2331     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2332      EL_EMC_MAGIC_BALL_SWITCH);
2333
2334   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2335     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2336   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2337     game.light_time_left;
2338
2339   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2340     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2341   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2342     game.timegate_time_left;
2343
2344   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2345     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2346
2347   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2348     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2349   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2350     game.lenses_time_left;
2351
2352   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2353     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2354   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2355     game.magnify_time_left;
2356
2357   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2358     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2359      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2360      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2361      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2362      EL_BALLOON_SWITCH_NONE);
2363
2364   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2365     local_player->dynabomb_count;
2366   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2367     local_player->dynabomb_size;
2368   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2369     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2370
2371   game_panel_controls[GAME_PANEL_PENGUINS].value =
2372     local_player->friends_still_needed;
2373
2374   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2375     local_player->sokobanfields_still_needed;
2376   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2377     local_player->sokobanfields_still_needed;
2378
2379   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2380     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2381
2382   for (i = 0; i < NUM_BELTS; i++)
2383   {
2384     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2385       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2386        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2387     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2388       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2389   }
2390
2391   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2392     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2393   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2394     game.magic_wall_time_left;
2395
2396 #if USE_PLAYER_GRAVITY
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398     local_player->gravity;
2399 #else
2400   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2401 #endif
2402
2403   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2404     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2405
2406   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2407     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2408       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2409        game.panel.element[i].id : EL_UNDEFINED);
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2413       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2414        element_info[game.panel.element_count[i].id].element_count : 0);
2415
2416   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2417     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2418       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2419        element_info[game.panel.ce_score[i].id].collect_score : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2424        element_info[game.panel.ce_score_element[i].id].collect_score :
2425        EL_UNDEFINED);
2426
2427   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2428   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2429   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2430
2431   /* update game panel control frames */
2432
2433   for (i = 0; game_panel_controls[i].nr != -1; i++)
2434   {
2435     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2436
2437     if (gpc->type == TYPE_ELEMENT)
2438     {
2439       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2440       {
2441         int last_anim_random_frame = gfx.anim_random_frame;
2442         int element = gpc->value;
2443         int graphic = el2panelimg(element);
2444
2445         if (gpc->value != gpc->last_value)
2446         {
2447           gpc->gfx_frame = 0;
2448           gpc->gfx_random = INIT_GFX_RANDOM();
2449         }
2450         else
2451         {
2452           gpc->gfx_frame++;
2453
2454           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2455               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2456             gpc->gfx_random = INIT_GFX_RANDOM();
2457         }
2458
2459         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2460           gfx.anim_random_frame = gpc->gfx_random;
2461
2462         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2463           gpc->gfx_frame = element_info[element].collect_score;
2464
2465         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2466                                               gpc->gfx_frame);
2467
2468         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2469           gfx.anim_random_frame = last_anim_random_frame;
2470       }
2471     }
2472   }
2473 }
2474
2475 void DisplayGameControlValues()
2476 {
2477   boolean redraw_panel = FALSE;
2478   int i;
2479
2480   for (i = 0; game_panel_controls[i].nr != -1; i++)
2481   {
2482     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2483
2484     if (PANEL_DEACTIVATED(gpc->pos))
2485       continue;
2486
2487     if (gpc->value == gpc->last_value &&
2488         gpc->frame == gpc->last_frame)
2489       continue;
2490
2491     redraw_panel = TRUE;
2492   }
2493
2494   if (!redraw_panel)
2495     return;
2496
2497   /* copy default game door content to main double buffer */
2498   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2499              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2500
2501   /* redraw game control buttons */
2502 #if 1
2503   RedrawGameButtons();
2504 #else
2505   UnmapGameButtons();
2506   MapGameButtons();
2507 #endif
2508
2509   game_status = GAME_MODE_PSEUDO_PANEL;
2510
2511 #if 1
2512   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2513 #else
2514   for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 #endif
2516   {
2517 #if 1
2518     int nr = game_panel_order[i].nr;
2519     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2520 #else
2521     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2522     int nr = gpc->nr;
2523 #endif
2524     struct TextPosInfo *pos = gpc->pos;
2525     int type = gpc->type;
2526     int value = gpc->value;
2527     int frame = gpc->frame;
2528 #if 0
2529     int last_value = gpc->last_value;
2530     int last_frame = gpc->last_frame;
2531 #endif
2532     int size = pos->size;
2533     int font = pos->font;
2534     boolean draw_masked = pos->draw_masked;
2535     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2536
2537     if (PANEL_DEACTIVATED(pos))
2538       continue;
2539
2540 #if 0
2541     if (value == last_value && frame == last_frame)
2542       continue;
2543 #endif
2544
2545     gpc->last_value = value;
2546     gpc->last_frame = frame;
2547
2548 #if 0
2549     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2550 #endif
2551
2552     if (type == TYPE_INTEGER)
2553     {
2554       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2555           nr == GAME_PANEL_TIME)
2556       {
2557         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2558
2559         if (use_dynamic_size)           /* use dynamic number of digits */
2560         {
2561           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2562           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2563           int size2 = size1 + 1;
2564           int font1 = pos->font;
2565           int font2 = pos->font_alt;
2566
2567           size = (value < value_change ? size1 : size2);
2568           font = (value < value_change ? font1 : font2);
2569
2570 #if 0
2571           /* clear background if value just changed its size (dynamic digits) */
2572           if ((last_value < value_change) != (value < value_change))
2573           {
2574             int width1 = size1 * getFontWidth(font1);
2575             int width2 = size2 * getFontWidth(font2);
2576             int max_width = MAX(width1, width2);
2577             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2578
2579             pos->width = max_width;
2580
2581             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2582                                        max_width, max_height);
2583           }
2584 #endif
2585         }
2586       }
2587
2588 #if 1
2589       /* correct text size if "digits" is zero or less */
2590       if (size <= 0)
2591         size = strlen(int2str(value, size));
2592
2593       /* dynamically correct text alignment */
2594       pos->width = size * getFontWidth(font);
2595 #endif
2596
2597       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598                   int2str(value, size), font, mask_mode);
2599     }
2600     else if (type == TYPE_ELEMENT)
2601     {
2602       int element, graphic;
2603       Bitmap *src_bitmap;
2604       int src_x, src_y;
2605       int width, height;
2606       int dst_x = PANEL_XPOS(pos);
2607       int dst_y = PANEL_YPOS(pos);
2608
2609 #if 1
2610       if (value != EL_UNDEFINED && value != EL_EMPTY)
2611       {
2612         element = value;
2613         graphic = el2panelimg(value);
2614
2615         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2616
2617 #if 1
2618         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2619           size = TILESIZE;
2620 #endif
2621
2622         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2623                               &src_x, &src_y);
2624
2625         width  = graphic_info[graphic].width  * size / TILESIZE;
2626         height = graphic_info[graphic].height * size / TILESIZE;
2627
2628         if (draw_masked)
2629         {
2630           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2631                         dst_x - src_x, dst_y - src_y);
2632           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2633                            dst_x, dst_y);
2634         }
2635         else
2636         {
2637           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2638                      dst_x, dst_y);
2639         }
2640       }
2641 #else
2642       if (value == EL_UNDEFINED || value == EL_EMPTY)
2643       {
2644         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2645         graphic = el2panelimg(element);
2646
2647         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2648         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2649         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2650       }
2651       else
2652       {
2653         element = value;
2654         graphic = el2panelimg(value);
2655
2656         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2657       }
2658
2659       width  = graphic_info[graphic].width  * size / TILESIZE;
2660       height = graphic_info[graphic].height * size / TILESIZE;
2661
2662       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2663 #endif
2664     }
2665     else if (type == TYPE_STRING)
2666     {
2667       boolean active = (value != 0);
2668       char *state_normal = "off";
2669       char *state_active = "on";
2670       char *state = (active ? state_active : state_normal);
2671       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2672                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2673                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2674                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2675
2676       if (nr == GAME_PANEL_GRAVITY_STATE)
2677       {
2678         int font1 = pos->font;          /* (used for normal state) */
2679         int font2 = pos->font_alt;      /* (used for active state) */
2680 #if 0
2681         int size1 = strlen(state_normal);
2682         int size2 = strlen(state_active);
2683         int width1 = size1 * getFontWidth(font1);
2684         int width2 = size2 * getFontWidth(font2);
2685         int max_width = MAX(width1, width2);
2686         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2687
2688         pos->width = max_width;
2689
2690         /* clear background for values that may have changed its size */
2691         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2692                                    max_width, max_height);
2693 #endif
2694
2695         font = (active ? font2 : font1);
2696       }
2697
2698       if (s != NULL)
2699       {
2700         char *s_cut;
2701
2702 #if 1
2703         if (size <= 0)
2704         {
2705           /* don't truncate output if "chars" is zero or less */
2706           size = strlen(s);
2707
2708           /* dynamically correct text alignment */
2709           pos->width = size * getFontWidth(font);
2710         }
2711 #endif
2712
2713         s_cut = getStringCopyN(s, size);
2714
2715         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2716                     s_cut, font, mask_mode);
2717
2718         free(s_cut);
2719       }
2720     }
2721
2722     redraw_mask |= REDRAW_DOOR_1;
2723   }
2724
2725   game_status = GAME_MODE_PLAYING;
2726 }
2727
2728 void UpdateAndDisplayGameControlValues()
2729 {
2730   if (tape.warp_forward)
2731     return;
2732
2733   UpdateGameControlValues();
2734   DisplayGameControlValues();
2735 }
2736
2737 void DrawGameValue_Emeralds(int value)
2738 {
2739   struct TextPosInfo *pos = &game.panel.gems;
2740 #if 1
2741   int font_nr = pos->font;
2742 #else
2743   int font_nr = FONT_TEXT_2;
2744 #endif
2745   int font_width = getFontWidth(font_nr);
2746   int chars = pos->size;
2747
2748 #if 1
2749   return;       /* !!! USE NEW STUFF !!! */
2750 #endif
2751
2752   if (PANEL_DEACTIVATED(pos))
2753     return;
2754
2755   pos->width = chars * font_width;
2756
2757   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2758 }
2759
2760 void DrawGameValue_Dynamite(int value)
2761 {
2762   struct TextPosInfo *pos = &game.panel.inventory_count;
2763 #if 1
2764   int font_nr = pos->font;
2765 #else
2766   int font_nr = FONT_TEXT_2;
2767 #endif
2768   int font_width = getFontWidth(font_nr);
2769   int chars = pos->size;
2770
2771 #if 1
2772   return;       /* !!! USE NEW STUFF !!! */
2773 #endif
2774
2775   if (PANEL_DEACTIVATED(pos))
2776     return;
2777
2778   pos->width = chars * font_width;
2779
2780   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2781 }
2782
2783 void DrawGameValue_Score(int value)
2784 {
2785   struct TextPosInfo *pos = &game.panel.score;
2786 #if 1
2787   int font_nr = pos->font;
2788 #else
2789   int font_nr = FONT_TEXT_2;
2790 #endif
2791   int font_width = getFontWidth(font_nr);
2792   int chars = pos->size;
2793
2794 #if 1
2795   return;       /* !!! USE NEW STUFF !!! */
2796 #endif
2797
2798   if (PANEL_DEACTIVATED(pos))
2799     return;
2800
2801   pos->width = chars * font_width;
2802
2803   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2804 }
2805
2806 void DrawGameValue_Time(int value)
2807 {
2808   struct TextPosInfo *pos = &game.panel.time;
2809   static int last_value = -1;
2810   int chars1 = 3;
2811   int chars2 = 4;
2812   int chars = pos->size;
2813 #if 1
2814   int font1_nr = pos->font;
2815   int font2_nr = pos->font_alt;
2816 #else
2817   int font1_nr = FONT_TEXT_2;
2818   int font2_nr = FONT_TEXT_1;
2819 #endif
2820   int font_nr = font1_nr;
2821   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822
2823 #if 1
2824   return;       /* !!! USE NEW STUFF !!! */
2825 #endif
2826
2827   if (PANEL_DEACTIVATED(pos))
2828     return;
2829
2830   if (use_dynamic_chars)                /* use dynamic number of chars */
2831   {
2832     chars   = (value < 1000 ? chars1   : chars2);
2833     font_nr = (value < 1000 ? font1_nr : font2_nr);
2834   }
2835
2836   /* clear background if value just changed its size (dynamic chars only) */
2837   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838   {
2839     int width1 = chars1 * getFontWidth(font1_nr);
2840     int width2 = chars2 * getFontWidth(font2_nr);
2841     int max_width = MAX(width1, width2);
2842     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843
2844     pos->width = max_width;
2845
2846     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847                                max_width, max_height);
2848   }
2849
2850   pos->width = chars * getFontWidth(font_nr);
2851
2852   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2853
2854   last_value = value;
2855 }
2856
2857 void DrawGameValue_Level(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.level_number;
2860   int chars1 = 2;
2861   int chars2 = 3;
2862   int chars = pos->size;
2863 #if 1
2864   int font1_nr = pos->font;
2865   int font2_nr = pos->font_alt;
2866 #else
2867   int font1_nr = FONT_TEXT_2;
2868   int font2_nr = FONT_TEXT_1;
2869 #endif
2870   int font_nr = font1_nr;
2871   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2872
2873 #if 1
2874   return;       /* !!! USE NEW STUFF !!! */
2875 #endif
2876
2877   if (PANEL_DEACTIVATED(pos))
2878     return;
2879
2880   if (use_dynamic_chars)                /* use dynamic number of chars */
2881   {
2882     chars   = (level_nr < 100 ? chars1   : chars2);
2883     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2884   }
2885
2886   pos->width = chars * getFontWidth(font_nr);
2887
2888   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2889 }
2890
2891 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2892 {
2893 #if 0
2894   struct TextPosInfo *pos = &game.panel.keys;
2895 #endif
2896 #if 0
2897   int base_key_graphic = EL_KEY_1;
2898 #endif
2899   int i;
2900
2901 #if 1
2902   return;       /* !!! USE NEW STUFF !!! */
2903 #endif
2904
2905 #if 0
2906   if (PANEL_DEACTIVATED(pos))
2907     return;
2908 #endif
2909
2910 #if 0
2911   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2912     base_key_graphic = EL_EM_KEY_1;
2913 #endif
2914
2915 #if 0
2916   pos->width = 4 * MINI_TILEX;
2917 #endif
2918
2919 #if 1
2920   for (i = 0; i < MAX_NUM_KEYS; i++)
2921 #else
2922   /* currently only 4 of 8 possible keys are displayed */
2923   for (i = 0; i < STD_NUM_KEYS; i++)
2924 #endif
2925   {
2926 #if 1
2927     struct TextPosInfo *pos = &game.panel.key[i];
2928 #endif
2929     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2930     int src_y = DOOR_GFX_PAGEY1 + 123;
2931 #if 1
2932     int dst_x = PANEL_XPOS(pos);
2933     int dst_y = PANEL_YPOS(pos);
2934 #else
2935     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2936     int dst_y = PANEL_YPOS(pos);
2937 #endif
2938
2939 #if 1
2940     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2941                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2942                    EL_KEY_1) + i;
2943     int graphic = el2edimg(element);
2944 #endif
2945
2946 #if 1
2947     if (PANEL_DEACTIVATED(pos))
2948       continue;
2949 #endif
2950
2951 #if 0
2952     /* masked blit with tiles from half-size scaled bitmap does not work yet
2953        (no mask bitmap created for these sizes after loading and scaling) --
2954        solution: load without creating mask, scale, then create final mask */
2955
2956     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2957                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2958
2959     if (key[i])
2960     {
2961 #if 0
2962       int graphic = el2edimg(base_key_graphic + i);
2963 #endif
2964       Bitmap *src_bitmap;
2965       int src_x, src_y;
2966
2967       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2968
2969       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2970                     dst_x - src_x, dst_y - src_y);
2971       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2972                        dst_x, dst_y);
2973     }
2974 #else
2975 #if 1
2976     if (key[i])
2977       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2978     else
2979       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2980                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2981 #else
2982     if (key[i])
2983       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2984     else
2985       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2986                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2987 #endif
2988 #endif
2989   }
2990 }
2991
2992 #else
2993
2994 void DrawGameValue_Emeralds(int value)
2995 {
2996   int font_nr = FONT_TEXT_2;
2997   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2998
2999   if (PANEL_DEACTIVATED(game.panel.gems))
3000     return;
3001
3002   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3003 }
3004
3005 void DrawGameValue_Dynamite(int value)
3006 {
3007   int font_nr = FONT_TEXT_2;
3008   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3009
3010   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3011     return;
3012
3013   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3014 }
3015
3016 void DrawGameValue_Score(int value)
3017 {
3018   int font_nr = FONT_TEXT_2;
3019   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3020
3021   if (PANEL_DEACTIVATED(game.panel.score))
3022     return;
3023
3024   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3025 }
3026
3027 void DrawGameValue_Time(int value)
3028 {
3029   int font1_nr = FONT_TEXT_2;
3030 #if 1
3031   int font2_nr = FONT_TEXT_1;
3032 #else
3033   int font2_nr = FONT_LEVEL_NUMBER;
3034 #endif
3035   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3036   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3037
3038   if (PANEL_DEACTIVATED(game.panel.time))
3039     return;
3040
3041   /* clear background if value just changed its size */
3042   if (value == 999 || value == 1000)
3043     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3044
3045   if (value < 1000)
3046     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3047   else
3048     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3049 }
3050
3051 void DrawGameValue_Level(int value)
3052 {
3053   int font1_nr = FONT_TEXT_2;
3054 #if 1
3055   int font2_nr = FONT_TEXT_1;
3056 #else
3057   int font2_nr = FONT_LEVEL_NUMBER;
3058 #endif
3059
3060   if (PANEL_DEACTIVATED(game.panel.level))
3061     return;
3062
3063   if (level_nr < 100)
3064     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3065   else
3066     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3067 }
3068
3069 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3070 {
3071   int base_key_graphic = EL_KEY_1;
3072   int i;
3073
3074   if (PANEL_DEACTIVATED(game.panel.keys))
3075     return;
3076
3077   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3078     base_key_graphic = EL_EM_KEY_1;
3079
3080   /* currently only 4 of 8 possible keys are displayed */
3081   for (i = 0; i < STD_NUM_KEYS; i++)
3082   {
3083     int x = XX_KEYS + i * MINI_TILEX;
3084     int y = YY_KEYS;
3085
3086     if (key[i])
3087       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3088     else
3089       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3090                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3091   }
3092 }
3093
3094 #endif
3095
3096 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3097                        int key_bits)
3098 {
3099   int key[MAX_NUM_KEYS];
3100   int i;
3101
3102   /* prevent EM engine from updating time/score values parallel to GameWon() */
3103   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3104       local_player->LevelSolved)
3105     return;
3106
3107   for (i = 0; i < MAX_NUM_KEYS; i++)
3108     key[i] = key_bits & (1 << i);
3109
3110   DrawGameValue_Level(level_nr);
3111
3112   DrawGameValue_Emeralds(emeralds);
3113   DrawGameValue_Dynamite(dynamite);
3114   DrawGameValue_Score(score);
3115   DrawGameValue_Time(time);
3116
3117   DrawGameValue_Keys(key);
3118 }
3119
3120 void UpdateGameDoorValues()
3121 {
3122   UpdateGameControlValues();
3123 }
3124
3125 void DrawGameDoorValues()
3126 {
3127   DisplayGameControlValues();
3128 }
3129
3130 void DrawGameDoorValues_OLD()
3131 {
3132   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3133   int dynamite_value = 0;
3134   int score_value = (local_player->LevelSolved ? local_player->score_final :
3135                      local_player->score);
3136   int gems_value = local_player->gems_still_needed;
3137   int key_bits = 0;
3138   int i, j;
3139
3140   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3141   {
3142     DrawGameDoorValues_EM();
3143
3144     return;
3145   }
3146
3147   if (game.centered_player_nr == -1)
3148   {
3149     for (i = 0; i < MAX_PLAYERS; i++)
3150     {
3151       for (j = 0; j < MAX_NUM_KEYS; j++)
3152         if (stored_player[i].key[j])
3153           key_bits |= (1 << j);
3154
3155       dynamite_value += stored_player[i].inventory_size;
3156     }
3157   }
3158   else
3159   {
3160     int player_nr = game.centered_player_nr;
3161
3162     for (i = 0; i < MAX_NUM_KEYS; i++)
3163       if (stored_player[player_nr].key[i])
3164         key_bits |= (1 << i);
3165
3166     dynamite_value = stored_player[player_nr].inventory_size;
3167   }
3168
3169   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3170                     key_bits);
3171 }
3172
3173
3174 /*
3175   =============================================================================
3176   InitGameEngine()
3177   -----------------------------------------------------------------------------
3178   initialize game engine due to level / tape version number
3179   =============================================================================
3180 */
3181
3182 static void InitGameEngine()
3183 {
3184   int i, j, k, l, x, y;
3185
3186   /* set game engine from tape file when re-playing, else from level file */
3187   game.engine_version = (tape.playing ? tape.engine_version :
3188                          level.game_version);
3189
3190   /* ---------------------------------------------------------------------- */
3191   /* set flags for bugs and changes according to active game engine version */
3192   /* ---------------------------------------------------------------------- */
3193
3194   /*
3195     Summary of bugfix/change:
3196     Fixed handling for custom elements that change when pushed by the player.
3197
3198     Fixed/changed in version:
3199     3.1.0
3200
3201     Description:
3202     Before 3.1.0, custom elements that "change when pushing" changed directly
3203     after the player started pushing them (until then handled in "DigField()").
3204     Since 3.1.0, these custom elements are not changed until the "pushing"
3205     move of the element is finished (now handled in "ContinueMoving()").
3206
3207     Affected levels/tapes:
3208     The first condition is generally needed for all levels/tapes before version
3209     3.1.0, which might use the old behaviour before it was changed; known tapes
3210     that are affected are some tapes from the level set "Walpurgis Gardens" by
3211     Jamie Cullen.
3212     The second condition is an exception from the above case and is needed for
3213     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3214     above (including some development versions of 3.1.0), but before it was
3215     known that this change would break tapes like the above and was fixed in
3216     3.1.1, so that the changed behaviour was active although the engine version
3217     while recording maybe was before 3.1.0. There is at least one tape that is
3218     affected by this exception, which is the tape for the one-level set "Bug
3219     Machine" by Juergen Bonhagen.
3220   */
3221
3222   game.use_change_when_pushing_bug =
3223     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3224      !(tape.playing &&
3225        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3226        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3227
3228   /*
3229     Summary of bugfix/change:
3230     Fixed handling for blocking the field the player leaves when moving.
3231
3232     Fixed/changed in version:
3233     3.1.1
3234
3235     Description:
3236     Before 3.1.1, when "block last field when moving" was enabled, the field
3237     the player is leaving when moving was blocked for the time of the move,
3238     and was directly unblocked afterwards. This resulted in the last field
3239     being blocked for exactly one less than the number of frames of one player
3240     move. Additionally, even when blocking was disabled, the last field was
3241     blocked for exactly one frame.
3242     Since 3.1.1, due to changes in player movement handling, the last field
3243     is not blocked at all when blocking is disabled. When blocking is enabled,
3244     the last field is blocked for exactly the number of frames of one player
3245     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3246     last field is blocked for exactly one more than the number of frames of
3247     one player move.
3248
3249     Affected levels/tapes:
3250     (!!! yet to be determined -- probably many !!!)
3251   */
3252
3253   game.use_block_last_field_bug =
3254     (game.engine_version < VERSION_IDENT(3,1,1,0));
3255
3256   /*
3257     Summary of bugfix/change:
3258     Changed behaviour of CE changes with multiple changes per single frame.
3259
3260     Fixed/changed in version:
3261     3.2.0-6
3262
3263     Description:
3264     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3265     This resulted in race conditions where CEs seem to behave strange in some
3266     situations (where triggered CE changes were just skipped because there was
3267     already a CE change on that tile in the playfield in that engine frame).
3268     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3269     (The number of changes per frame must be limited in any case, because else
3270     it is easily possible to define CE changes that would result in an infinite
3271     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3272     should be set large enough so that it would only be reached in cases where
3273     the corresponding CE change conditions run into a loop. Therefore, it seems
3274     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3275     maximal number of change pages for custom elements.)
3276
3277     Affected levels/tapes:
3278     Probably many.
3279   */
3280
3281 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3282   game.max_num_changes_per_frame = 1;
3283 #else
3284   game.max_num_changes_per_frame =
3285     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3286 #endif
3287
3288   /* ---------------------------------------------------------------------- */
3289
3290   /* default scan direction: scan playfield from top/left to bottom/right */
3291   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3292
3293   /* dynamically adjust element properties according to game engine version */
3294   InitElementPropertiesEngine(game.engine_version);
3295
3296 #if 0
3297   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3298   printf("          tape version == %06d [%s] [file: %06d]\n",
3299          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3300          tape.file_version);
3301   printf("       => game.engine_version == %06d\n", game.engine_version);
3302 #endif
3303
3304   /* ---------- initialize player's initial move delay --------------------- */
3305
3306   /* dynamically adjust player properties according to level information */
3307   for (i = 0; i < MAX_PLAYERS; i++)
3308     game.initial_move_delay_value[i] =
3309       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3310
3311   /* dynamically adjust player properties according to game engine version */
3312   for (i = 0; i < MAX_PLAYERS; i++)
3313     game.initial_move_delay[i] =
3314       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3315        game.initial_move_delay_value[i] : 0);
3316
3317   /* ---------- initialize player's initial push delay --------------------- */
3318
3319   /* dynamically adjust player properties according to game engine version */
3320   game.initial_push_delay_value =
3321     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3322
3323   /* ---------- initialize changing elements ------------------------------- */
3324
3325   /* initialize changing elements information */
3326   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327   {
3328     struct ElementInfo *ei = &element_info[i];
3329
3330     /* this pointer might have been changed in the level editor */
3331     ei->change = &ei->change_page[0];
3332
3333     if (!IS_CUSTOM_ELEMENT(i))
3334     {
3335       ei->change->target_element = EL_EMPTY_SPACE;
3336       ei->change->delay_fixed = 0;
3337       ei->change->delay_random = 0;
3338       ei->change->delay_frames = 1;
3339     }
3340
3341     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3342     {
3343       ei->has_change_event[j] = FALSE;
3344
3345       ei->event_page_nr[j] = 0;
3346       ei->event_page[j] = &ei->change_page[0];
3347     }
3348   }
3349
3350   /* add changing elements from pre-defined list */
3351   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3352   {
3353     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3354     struct ElementInfo *ei = &element_info[ch_delay->element];
3355
3356     ei->change->target_element       = ch_delay->target_element;
3357     ei->change->delay_fixed          = ch_delay->change_delay;
3358
3359     ei->change->pre_change_function  = ch_delay->pre_change_function;
3360     ei->change->change_function      = ch_delay->change_function;
3361     ei->change->post_change_function = ch_delay->post_change_function;
3362
3363     ei->change->can_change = TRUE;
3364     ei->change->can_change_or_has_action = TRUE;
3365
3366     ei->has_change_event[CE_DELAY] = TRUE;
3367
3368     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3369     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3370   }
3371
3372   /* ---------- initialize internal run-time variables --------------------- */
3373
3374   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3375   {
3376     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3377
3378     for (j = 0; j < ei->num_change_pages; j++)
3379     {
3380       ei->change_page[j].can_change_or_has_action =
3381         (ei->change_page[j].can_change |
3382          ei->change_page[j].has_action);
3383     }
3384   }
3385
3386   /* add change events from custom element configuration */
3387   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3388   {
3389     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3390
3391     for (j = 0; j < ei->num_change_pages; j++)
3392     {
3393       if (!ei->change_page[j].can_change_or_has_action)
3394         continue;
3395
3396       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3397       {
3398         /* only add event page for the first page found with this event */
3399         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3400         {
3401           ei->has_change_event[k] = TRUE;
3402
3403           ei->event_page_nr[k] = j;
3404           ei->event_page[k] = &ei->change_page[j];
3405         }
3406       }
3407     }
3408   }
3409
3410 #if 1
3411   /* ---------- initialize reference elements in change conditions --------- */
3412
3413   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3414   {
3415     int element = EL_CUSTOM_START + i;
3416     struct ElementInfo *ei = &element_info[element];
3417
3418     for (j = 0; j < ei->num_change_pages; j++)
3419     {
3420       int trigger_element = ei->change_page[j].initial_trigger_element;
3421
3422       if (trigger_element >= EL_PREV_CE_8 &&
3423           trigger_element <= EL_NEXT_CE_8)
3424         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3425
3426       ei->change_page[j].trigger_element = trigger_element;
3427     }
3428   }
3429 #endif
3430
3431   /* ---------- initialize run-time trigger player and element ------------- */
3432
3433   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3434   {
3435     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3436
3437     for (j = 0; j < ei->num_change_pages; j++)
3438     {
3439       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3440       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3441       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3442       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3443       ei->change_page[j].actual_trigger_ce_value = 0;
3444       ei->change_page[j].actual_trigger_ce_score = 0;
3445     }
3446   }
3447
3448   /* ---------- initialize trigger events ---------------------------------- */
3449
3450   /* initialize trigger events information */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3453       trigger_events[i][j] = FALSE;
3454
3455   /* add trigger events from element change event properties */
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457   {
3458     struct ElementInfo *ei = &element_info[i];
3459
3460     for (j = 0; j < ei->num_change_pages; j++)
3461     {
3462       if (!ei->change_page[j].can_change_or_has_action)
3463         continue;
3464
3465       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3466       {
3467         int trigger_element = ei->change_page[j].trigger_element;
3468
3469         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3470         {
3471           if (ei->change_page[j].has_event[k])
3472           {
3473             if (IS_GROUP_ELEMENT(trigger_element))
3474             {
3475               struct ElementGroupInfo *group =
3476                 element_info[trigger_element].group;
3477
3478               for (l = 0; l < group->num_elements_resolved; l++)
3479                 trigger_events[group->element_resolved[l]][k] = TRUE;
3480             }
3481             else if (trigger_element == EL_ANY_ELEMENT)
3482               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3483                 trigger_events[l][k] = TRUE;
3484             else
3485               trigger_events[trigger_element][k] = TRUE;
3486           }
3487         }
3488       }
3489     }
3490   }
3491
3492   /* ---------- initialize push delay -------------------------------------- */
3493
3494   /* initialize push delay values to default */
3495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496   {
3497     if (!IS_CUSTOM_ELEMENT(i))
3498     {
3499       /* set default push delay values (corrected since version 3.0.7-1) */
3500       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3501       {
3502         element_info[i].push_delay_fixed = 2;
3503         element_info[i].push_delay_random = 8;
3504       }
3505       else
3506       {
3507         element_info[i].push_delay_fixed = 8;
3508         element_info[i].push_delay_random = 8;
3509       }
3510     }
3511   }
3512
3513   /* set push delay value for certain elements from pre-defined list */
3514   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3515   {
3516     int e = push_delay_list[i].element;
3517
3518     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3519     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3520   }
3521
3522   /* set push delay value for Supaplex elements for newer engine versions */
3523   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3524   {
3525     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3526     {
3527       if (IS_SP_ELEMENT(i))
3528       {
3529         /* set SP push delay to just enough to push under a falling zonk */
3530         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3531
3532         element_info[i].push_delay_fixed  = delay;
3533         element_info[i].push_delay_random = 0;
3534       }
3535     }
3536   }
3537
3538   /* ---------- initialize move stepsize ----------------------------------- */
3539
3540   /* initialize move stepsize values to default */
3541   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3542     if (!IS_CUSTOM_ELEMENT(i))
3543       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3544
3545   /* set move stepsize value for certain elements from pre-defined list */
3546   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3547   {
3548     int e = move_stepsize_list[i].element;
3549
3550     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3551   }
3552
3553   /* ---------- initialize collect score ----------------------------------- */
3554
3555   /* initialize collect score values for custom elements from initial value */
3556   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3557     if (IS_CUSTOM_ELEMENT(i))
3558       element_info[i].collect_score = element_info[i].collect_score_initial;
3559
3560   /* ---------- initialize collect count ----------------------------------- */
3561
3562   /* initialize collect count values for non-custom elements */
3563   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3564     if (!IS_CUSTOM_ELEMENT(i))
3565       element_info[i].collect_count_initial = 0;
3566
3567   /* add collect count values for all elements from pre-defined list */
3568   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3569     element_info[collect_count_list[i].element].collect_count_initial =
3570       collect_count_list[i].count;
3571
3572   /* ---------- initialize access direction -------------------------------- */
3573
3574   /* initialize access direction values to default (access from every side) */
3575   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3576     if (!IS_CUSTOM_ELEMENT(i))
3577       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3578
3579   /* set access direction value for certain elements from pre-defined list */
3580   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3581     element_info[access_direction_list[i].element].access_direction =
3582       access_direction_list[i].direction;
3583
3584   /* ---------- initialize explosion content ------------------------------- */
3585   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586   {
3587     if (IS_CUSTOM_ELEMENT(i))
3588       continue;
3589
3590     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3591     {
3592       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3593
3594       element_info[i].content.e[x][y] =
3595         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3596          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3597          i == EL_PLAYER_3 ? EL_EMERALD :
3598          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3599          i == EL_MOLE ? EL_EMERALD_RED :
3600          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3601          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3602          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3603          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3604          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3605          i == EL_WALL_EMERALD ? EL_EMERALD :
3606          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3607          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3608          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3609          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3610          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3611          i == EL_WALL_PEARL ? EL_PEARL :
3612          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3613          EL_EMPTY);
3614     }
3615   }
3616
3617   /* ---------- initialize recursion detection ------------------------------ */
3618   recursion_loop_depth = 0;
3619   recursion_loop_detected = FALSE;
3620   recursion_loop_element = EL_UNDEFINED;
3621
3622   /* ---------- initialize graphics engine ---------------------------------- */
3623   game.scroll_delay_value =
3624     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3625      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3626   game.scroll_delay_value =
3627     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3628 }
3629
3630 int get_num_special_action(int element, int action_first, int action_last)
3631 {
3632   int num_special_action = 0;
3633   int i, j;
3634
3635   for (i = action_first; i <= action_last; i++)
3636   {
3637     boolean found = FALSE;
3638
3639     for (j = 0; j < NUM_DIRECTIONS; j++)
3640       if (el_act_dir2img(element, i, j) !=
3641           el_act_dir2img(element, ACTION_DEFAULT, j))
3642         found = TRUE;
3643
3644     if (found)
3645       num_special_action++;
3646     else
3647       break;
3648   }
3649
3650   return num_special_action;
3651 }
3652
3653
3654 /*
3655   =============================================================================
3656   InitGame()
3657   -----------------------------------------------------------------------------
3658   initialize and start new game
3659   =============================================================================
3660 */
3661
3662 void InitGame()
3663 {
3664   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3665   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3666   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3667 #if 0
3668   boolean do_fading = (game_status == GAME_MODE_MAIN);
3669 #endif
3670   int i, j, x, y;
3671
3672   game_status = GAME_MODE_PLAYING;
3673
3674   InitGameEngine();
3675   InitGameControlValues();
3676
3677   /* don't play tapes over network */
3678   network_playing = (options.network && !tape.playing);
3679
3680   for (i = 0; i < MAX_PLAYERS; i++)
3681   {
3682     struct PlayerInfo *player = &stored_player[i];
3683
3684     player->index_nr = i;
3685     player->index_bit = (1 << i);
3686     player->element_nr = EL_PLAYER_1 + i;
3687
3688     player->present = FALSE;
3689     player->active = FALSE;
3690     player->killed = FALSE;
3691
3692     player->action = 0;
3693     player->effective_action = 0;
3694     player->programmed_action = 0;
3695
3696     player->score = 0;
3697     player->score_final = 0;
3698
3699     player->gems_still_needed = level.gems_needed;
3700     player->sokobanfields_still_needed = 0;
3701     player->lights_still_needed = 0;
3702     player->friends_still_needed = 0;
3703
3704     for (j = 0; j < MAX_NUM_KEYS; j++)
3705       player->key[j] = FALSE;
3706
3707     player->num_white_keys = 0;
3708
3709     player->dynabomb_count = 0;
3710     player->dynabomb_size = 1;
3711     player->dynabombs_left = 0;
3712     player->dynabomb_xl = FALSE;
3713
3714     player->MovDir = MV_NONE;
3715     player->MovPos = 0;
3716     player->GfxPos = 0;
3717     player->GfxDir = MV_NONE;
3718     player->GfxAction = ACTION_DEFAULT;
3719     player->Frame = 0;
3720     player->StepFrame = 0;
3721
3722     player->initial_element = player->element_nr;
3723     player->artwork_element =
3724       (level.use_artwork_element[i] ? level.artwork_element[i] :
3725        player->element_nr);
3726     player->use_murphy = FALSE;
3727
3728     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3729     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3730
3731     player->gravity = level.initial_player_gravity[i];
3732
3733     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3734
3735     player->actual_frame_counter = 0;
3736
3737     player->step_counter = 0;
3738
3739     player->last_move_dir = MV_NONE;
3740
3741     player->is_active = FALSE;
3742
3743     player->is_waiting = FALSE;
3744     player->is_moving = FALSE;
3745     player->is_auto_moving = FALSE;
3746     player->is_digging = FALSE;
3747     player->is_snapping = FALSE;
3748     player->is_collecting = FALSE;
3749     player->is_pushing = FALSE;
3750     player->is_switching = FALSE;
3751     player->is_dropping = FALSE;
3752     player->is_dropping_pressed = FALSE;
3753
3754     player->is_bored = FALSE;
3755     player->is_sleeping = FALSE;
3756
3757     player->frame_counter_bored = -1;
3758     player->frame_counter_sleeping = -1;
3759
3760     player->anim_delay_counter = 0;
3761     player->post_delay_counter = 0;
3762
3763     player->dir_waiting = MV_NONE;
3764     player->action_waiting = ACTION_DEFAULT;
3765     player->last_action_waiting = ACTION_DEFAULT;
3766     player->special_action_bored = ACTION_DEFAULT;
3767     player->special_action_sleeping = ACTION_DEFAULT;
3768
3769     player->switch_x = -1;
3770     player->switch_y = -1;
3771
3772     player->drop_x = -1;
3773     player->drop_y = -1;
3774
3775     player->show_envelope = 0;
3776
3777     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3778
3779     player->push_delay       = -1;      /* initialized when pushing starts */
3780     player->push_delay_value = game.initial_push_delay_value;
3781
3782     player->drop_delay = 0;
3783     player->drop_pressed_delay = 0;
3784
3785     player->last_jx = -1;
3786     player->last_jy = -1;
3787     player->jx = -1;
3788     player->jy = -1;
3789
3790     player->shield_normal_time_left = 0;
3791     player->shield_deadly_time_left = 0;
3792
3793     player->inventory_infinite_element = EL_UNDEFINED;
3794     player->inventory_size = 0;
3795
3796     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3797     SnapField(player, 0, 0);
3798
3799     player->LevelSolved = FALSE;
3800     player->GameOver = FALSE;
3801
3802     player->LevelSolved_GameWon = FALSE;
3803     player->LevelSolved_GameEnd = FALSE;
3804     player->LevelSolved_PanelOff = FALSE;
3805     player->LevelSolved_SaveTape = FALSE;
3806     player->LevelSolved_SaveScore = FALSE;
3807     player->LevelSolved_CountingTime = 0;
3808     player->LevelSolved_CountingScore = 0;
3809   }
3810
3811   network_player_action_received = FALSE;
3812
3813 #if defined(NETWORK_AVALIABLE)
3814   /* initial null action */
3815   if (network_playing)
3816     SendToServer_MovePlayer(MV_NONE);
3817 #endif
3818
3819   ZX = ZY = -1;
3820   ExitX = ExitY = -1;
3821
3822   FrameCounter = 0;
3823   TimeFrames = 0;
3824   TimePlayed = 0;
3825   TimeLeft = level.time;
3826   TapeTime = 0;
3827
3828   ScreenMovDir = MV_NONE;
3829   ScreenMovPos = 0;
3830   ScreenGfxPos = 0;
3831
3832   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3833
3834   AllPlayersGone = FALSE;
3835
3836   game.yamyam_content_nr = 0;
3837   game.robot_wheel_active = FALSE;
3838   game.magic_wall_active = FALSE;
3839   game.magic_wall_time_left = 0;
3840   game.light_time_left = 0;
3841   game.timegate_time_left = 0;
3842   game.switchgate_pos = 0;
3843   game.wind_direction = level.wind_direction_initial;
3844
3845 #if !USE_PLAYER_GRAVITY
3846   game.gravity = FALSE;
3847   game.explosions_delayed = TRUE;
3848 #endif
3849
3850   game.lenses_time_left = 0;
3851   game.magnify_time_left = 0;
3852
3853   game.ball_state = level.ball_state_initial;
3854   game.ball_content_nr = 0;
3855
3856   game.envelope_active = FALSE;
3857
3858   /* set focus to local player for network games, else to all players */
3859   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3860   game.centered_player_nr_next = game.centered_player_nr;
3861   game.set_centered_player = FALSE;
3862
3863   if (network_playing && tape.recording)
3864   {
3865     /* store client dependent player focus when recording network games */
3866     tape.centered_player_nr_next = game.centered_player_nr_next;
3867     tape.set_centered_player = TRUE;
3868   }
3869
3870   for (i = 0; i < NUM_BELTS; i++)
3871   {
3872     game.belt_dir[i] = MV_NONE;
3873     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3874   }
3875
3876   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3877     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3878
3879   SCAN_PLAYFIELD(x, y)
3880   {
3881     Feld[x][y] = level.field[x][y];
3882     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3883     ChangeDelay[x][y] = 0;
3884     ChangePage[x][y] = -1;
3885 #if USE_NEW_CUSTOM_VALUE
3886     CustomValue[x][y] = 0;              /* initialized in InitField() */
3887 #endif
3888     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3889     AmoebaNr[x][y] = 0;
3890     WasJustMoving[x][y] = 0;
3891     WasJustFalling[x][y] = 0;
3892     CheckCollision[x][y] = 0;
3893     CheckImpact[x][y] = 0;
3894     Stop[x][y] = FALSE;
3895     Pushed[x][y] = FALSE;
3896
3897     ChangeCount[x][y] = 0;
3898     ChangeEvent[x][y] = -1;
3899
3900     ExplodePhase[x][y] = 0;
3901     ExplodeDelay[x][y] = 0;
3902     ExplodeField[x][y] = EX_TYPE_NONE;
3903
3904     RunnerVisit[x][y] = 0;
3905     PlayerVisit[x][y] = 0;
3906
3907     GfxFrame[x][y] = 0;
3908     GfxRandom[x][y] = INIT_GFX_RANDOM();
3909     GfxElement[x][y] = EL_UNDEFINED;
3910     GfxAction[x][y] = ACTION_DEFAULT;
3911     GfxDir[x][y] = MV_NONE;
3912     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3913   }
3914
3915   SCAN_PLAYFIELD(x, y)
3916   {
3917     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3918       emulate_bd = FALSE;
3919     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3920       emulate_sb = FALSE;
3921     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3922       emulate_sp = FALSE;
3923
3924     InitField(x, y, TRUE);
3925
3926     ResetGfxAnimation(x, y);
3927   }
3928
3929   InitBeltMovement();
3930
3931   for (i = 0; i < MAX_PLAYERS; i++)
3932   {
3933     struct PlayerInfo *player = &stored_player[i];
3934
3935     /* set number of special actions for bored and sleeping animation */
3936     player->num_special_action_bored =
3937       get_num_special_action(player->artwork_element,
3938                              ACTION_BORING_1, ACTION_BORING_LAST);
3939     player->num_special_action_sleeping =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3942   }
3943
3944   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3945                     emulate_sb ? EMU_SOKOBAN :
3946                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3947
3948 #if USE_NEW_ALL_SLIPPERY
3949   /* initialize type of slippery elements */
3950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3951   {
3952     if (!IS_CUSTOM_ELEMENT(i))
3953     {
3954       /* default: elements slip down either to the left or right randomly */
3955       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3956
3957       /* SP style elements prefer to slip down on the left side */
3958       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3959         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3960
3961       /* BD style elements prefer to slip down on the left side */
3962       if (game.emulation == EMU_BOULDERDASH)
3963         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3964     }
3965   }
3966 #endif
3967
3968   /* initialize explosion and ignition delay */
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986
3987 #if 0
3988     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3989       element_info[i].explosion_delay = 1;
3990
3991     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3992       element_info[i].ignition_delay = 1;
3993 #endif
3994   }
3995
3996   /* correct non-moving belts to start moving left */
3997   for (i = 0; i < NUM_BELTS; i++)
3998     if (game.belt_dir[i] == MV_NONE)
3999       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4000
4001   /* check if any connected player was not found in playfield */
4002   for (i = 0; i < MAX_PLAYERS; i++)
4003   {
4004     struct PlayerInfo *player = &stored_player[i];
4005
4006     if (player->connected && !player->present)
4007     {
4008       for (j = 0; j < MAX_PLAYERS; j++)
4009       {
4010         struct PlayerInfo *some_player = &stored_player[j];
4011         int jx = some_player->jx, jy = some_player->jy;
4012
4013         /* assign first free player found that is present in the playfield */
4014         if (some_player->present && !some_player->connected)
4015         {
4016           player->present = TRUE;
4017           player->active = TRUE;
4018
4019           some_player->present = FALSE;
4020           some_player->active = FALSE;
4021
4022           player->initial_element = some_player->initial_element;
4023           player->artwork_element = some_player->artwork_element;
4024
4025           player->block_last_field       = some_player->block_last_field;
4026           player->block_delay_adjustment = some_player->block_delay_adjustment;
4027
4028           StorePlayer[jx][jy] = player->element_nr;
4029           player->jx = player->last_jx = jx;
4030           player->jy = player->last_jy = jy;
4031
4032           break;
4033         }
4034       }
4035     }
4036   }
4037
4038   if (tape.playing)
4039   {
4040     /* when playing a tape, eliminate all players who do not participate */
4041
4042     for (i = 0; i < MAX_PLAYERS; i++)
4043     {
4044       if (stored_player[i].active && !tape.player_participates[i])
4045       {
4046         struct PlayerInfo *player = &stored_player[i];
4047         int jx = player->jx, jy = player->jy;
4048
4049         player->active = FALSE;
4050         StorePlayer[jx][jy] = 0;
4051         Feld[jx][jy] = EL_EMPTY;
4052       }
4053     }
4054   }
4055   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4056   {
4057     /* when in single player mode, eliminate all but the first active player */
4058
4059     for (i = 0; i < MAX_PLAYERS; i++)
4060     {
4061       if (stored_player[i].active)
4062       {
4063         for (j = i + 1; j < MAX_PLAYERS; j++)
4064         {
4065           if (stored_player[j].active)
4066           {
4067             struct PlayerInfo *player = &stored_player[j];
4068             int jx = player->jx, jy = player->jy;
4069
4070             player->active = FALSE;
4071             player->present = FALSE;
4072
4073             StorePlayer[jx][jy] = 0;
4074             Feld[jx][jy] = EL_EMPTY;
4075           }
4076         }
4077       }
4078     }
4079   }
4080
4081   /* when recording the game, store which players take part in the game */
4082   if (tape.recording)
4083   {
4084     for (i = 0; i < MAX_PLAYERS; i++)
4085       if (stored_player[i].active)
4086         tape.player_participates[i] = TRUE;
4087   }
4088
4089   if (options.debug)
4090   {
4091     for (i = 0; i < MAX_PLAYERS; i++)
4092     {
4093       struct PlayerInfo *player = &stored_player[i];
4094
4095       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4096              i+1,
4097              player->present,
4098              player->connected,
4099              player->active);
4100       if (local_player == player)
4101         printf("Player  %d is local player.\n", i+1);
4102     }
4103   }
4104
4105   if (BorderElement == EL_EMPTY)
4106   {
4107     SBX_Left = 0;
4108     SBX_Right = lev_fieldx - SCR_FIELDX;
4109     SBY_Upper = 0;
4110     SBY_Lower = lev_fieldy - SCR_FIELDY;
4111   }
4112   else
4113   {
4114     SBX_Left = -1;
4115     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4116     SBY_Upper = -1;
4117     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4118   }
4119
4120   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4121     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4122
4123   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4124     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4125
4126   /* if local player not found, look for custom element that might create
4127      the player (make some assumptions about the right custom element) */
4128   if (!local_player->present)
4129   {
4130     int start_x = 0, start_y = 0;
4131     int found_rating = 0;
4132     int found_element = EL_UNDEFINED;
4133     int player_nr = local_player->index_nr;
4134
4135     SCAN_PLAYFIELD(x, y)
4136     {
4137       int element = Feld[x][y];
4138       int content;
4139       int xx, yy;
4140       boolean is_player;
4141
4142       if (level.use_start_element[player_nr] &&
4143           level.start_element[player_nr] == element &&
4144           found_rating < 4)
4145       {
4146         start_x = x;
4147         start_y = y;
4148
4149         found_rating = 4;
4150         found_element = element;
4151       }
4152
4153       if (!IS_CUSTOM_ELEMENT(element))
4154         continue;
4155
4156       if (CAN_CHANGE(element))
4157       {
4158         for (i = 0; i < element_info[element].num_change_pages; i++)
4159         {
4160           /* check for player created from custom element as single target */
4161           content = element_info[element].change_page[i].target_element;
4162           is_player = ELEM_IS_PLAYER(content);
4163
4164           if (is_player && (found_rating < 3 ||
4165                             (found_rating == 3 && element < found_element)))
4166           {
4167             start_x = x;
4168             start_y = y;
4169
4170             found_rating = 3;
4171             found_element = element;
4172           }
4173         }
4174       }
4175
4176       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4177       {
4178         /* check for player created from custom element as explosion content */
4179         content = element_info[element].content.e[xx][yy];
4180         is_player = ELEM_IS_PLAYER(content);
4181
4182         if (is_player && (found_rating < 2 ||
4183                           (found_rating == 2 && element < found_element)))
4184         {
4185           start_x = x + xx - 1;
4186           start_y = y + yy - 1;
4187
4188           found_rating = 2;
4189           found_element = element;
4190         }
4191
4192         if (!CAN_CHANGE(element))
4193           continue;
4194
4195         for (i = 0; i < element_info[element].num_change_pages; i++)
4196         {
4197           /* check for player created from custom element as extended target */
4198           content =
4199             element_info[element].change_page[i].target_content.e[xx][yy];
4200
4201           is_player = ELEM_IS_PLAYER(content);
4202
4203           if (is_player && (found_rating < 1 ||
4204                             (found_rating == 1 && element < found_element)))
4205           {
4206             start_x = x + xx - 1;
4207             start_y = y + yy - 1;
4208
4209             found_rating = 1;
4210             found_element = element;
4211           }
4212         }
4213       }
4214     }
4215
4216     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4217                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4218                 start_x - MIDPOSX);
4219
4220     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4221                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4222                 start_y - MIDPOSY);
4223   }
4224   else
4225   {
4226     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4227                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4228                 local_player->jx - MIDPOSX);
4229
4230     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4231                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4232                 local_player->jy - MIDPOSY);
4233   }
4234
4235 #if 0
4236   /* do not use PLAYING mask for fading out from main screen */
4237   game_status = GAME_MODE_MAIN;
4238 #endif
4239
4240   StopAnimation();
4241
4242   if (!game.restart_level)
4243     CloseDoor(DOOR_CLOSE_1);
4244
4245 #if 1
4246   if (level_editor_test_game)
4247     FadeSkipNextFadeIn();
4248   else
4249     FadeSetEnterScreen();
4250 #else
4251   if (level_editor_test_game)
4252     fading = fading_none;
4253   else
4254     fading = menu.destination;
4255 #endif
4256
4257 #if 1
4258   FadeOut(REDRAW_FIELD);
4259 #else
4260   if (do_fading)
4261     FadeOut(REDRAW_FIELD);
4262 #endif
4263
4264 #if 0
4265   game_status = GAME_MODE_PLAYING;
4266 #endif
4267
4268   /* !!! FIX THIS (START) !!! */
4269   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4270   {
4271     InitGameEngine_EM();
4272
4273     /* blit playfield from scroll buffer to normal back buffer for fading in */
4274     BlitScreenToBitmap_EM(backbuffer);
4275   }
4276   else
4277   {
4278     DrawLevel();
4279     DrawAllPlayers();
4280
4281     /* after drawing the level, correct some elements */
4282     if (game.timegate_time_left == 0)
4283       CloseAllOpenTimegates();
4284
4285     /* blit playfield from scroll buffer to normal back buffer for fading in */
4286     if (setup.soft_scrolling)
4287       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4288
4289     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4290   }
4291   /* !!! FIX THIS (END) !!! */
4292
4293 #if 1
4294   FadeIn(REDRAW_FIELD);
4295 #else
4296   if (do_fading)
4297     FadeIn(REDRAW_FIELD);
4298
4299   BackToFront();
4300 #endif
4301
4302   if (!game.restart_level)
4303   {
4304     /* copy default game door content to main double buffer */
4305     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4306                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4307   }
4308
4309   SetPanelBackground();
4310   SetDrawBackgroundMask(REDRAW_DOOR_1);
4311
4312 #if 1
4313   UpdateAndDisplayGameControlValues();
4314 #else
4315   UpdateGameDoorValues();
4316   DrawGameDoorValues();
4317 #endif
4318
4319   if (!game.restart_level)
4320   {
4321     UnmapGameButtons();
4322     UnmapTapeButtons();
4323     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4324     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4325     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4326     MapGameButtons();
4327     MapTapeButtons();
4328
4329     /* copy actual game door content to door double buffer for OpenDoor() */
4330     BlitBitmap(drawto, bitmap_db_door,
4331                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4332
4333     OpenDoor(DOOR_OPEN_ALL);
4334
4335     PlaySound(SND_GAME_STARTING);
4336
4337     if (setup.sound_music)
4338       PlayLevelMusic();
4339
4340     KeyboardAutoRepeatOffUnlessAutoplay();
4341
4342     if (options.debug)
4343     {
4344       for (i = 0; i < MAX_PLAYERS; i++)
4345         printf("Player %d %sactive.\n",
4346                i + 1, (stored_player[i].active ? "" : "not "));
4347     }
4348   }
4349
4350 #if 1
4351   UnmapAllGadgets();
4352
4353   MapGameButtons();
4354   MapTapeButtons();
4355 #endif
4356
4357   game.restart_level = FALSE;
4358 }
4359
4360 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4361 {
4362   /* this is used for non-R'n'D game engines to update certain engine values */
4363
4364   /* needed to determine if sounds are played within the visible screen area */
4365   scroll_x = actual_scroll_x;
4366   scroll_y = actual_scroll_y;
4367 }
4368
4369 void InitMovDir(int x, int y)
4370 {
4371   int i, element = Feld[x][y];
4372   static int xy[4][2] =
4373   {
4374     {  0, +1 },
4375     { +1,  0 },
4376     {  0, -1 },
4377     { -1,  0 }
4378   };
4379   static int direction[3][4] =
4380   {
4381     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4382     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4383     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4384   };
4385
4386   switch (element)
4387   {
4388     case EL_BUG_RIGHT:
4389     case EL_BUG_UP:
4390     case EL_BUG_LEFT:
4391     case EL_BUG_DOWN:
4392       Feld[x][y] = EL_BUG;
4393       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4394       break;
4395
4396     case EL_SPACESHIP_RIGHT:
4397     case EL_SPACESHIP_UP:
4398     case EL_SPACESHIP_LEFT:
4399     case EL_SPACESHIP_DOWN:
4400       Feld[x][y] = EL_SPACESHIP;
4401       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4402       break;
4403
4404     case EL_BD_BUTTERFLY_RIGHT:
4405     case EL_BD_BUTTERFLY_UP:
4406     case EL_BD_BUTTERFLY_LEFT:
4407     case EL_BD_BUTTERFLY_DOWN:
4408       Feld[x][y] = EL_BD_BUTTERFLY;
4409       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4410       break;
4411
4412     case EL_BD_FIREFLY_RIGHT:
4413     case EL_BD_FIREFLY_UP:
4414     case EL_BD_FIREFLY_LEFT:
4415     case EL_BD_FIREFLY_DOWN:
4416       Feld[x][y] = EL_BD_FIREFLY;
4417       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4418       break;
4419
4420     case EL_PACMAN_RIGHT:
4421     case EL_PACMAN_UP:
4422     case EL_PACMAN_LEFT:
4423     case EL_PACMAN_DOWN:
4424       Feld[x][y] = EL_PACMAN;
4425       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4426       break;
4427
4428     case EL_YAMYAM_LEFT:
4429     case EL_YAMYAM_RIGHT:
4430     case EL_YAMYAM_UP:
4431     case EL_YAMYAM_DOWN:
4432       Feld[x][y] = EL_YAMYAM;
4433       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4434       break;
4435
4436     case EL_SP_SNIKSNAK:
4437       MovDir[x][y] = MV_UP;
4438       break;
4439
4440     case EL_SP_ELECTRON:
4441       MovDir[x][y] = MV_LEFT;
4442       break;
4443
4444     case EL_MOLE_LEFT:
4445     case EL_MOLE_RIGHT:
4446     case EL_MOLE_UP:
4447     case EL_MOLE_DOWN:
4448       Feld[x][y] = EL_MOLE;
4449       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4450       break;
4451
4452     default:
4453       if (IS_CUSTOM_ELEMENT(element))
4454       {
4455         struct ElementInfo *ei = &element_info[element];
4456         int move_direction_initial = ei->move_direction_initial;
4457         int move_pattern = ei->move_pattern;
4458
4459         if (move_direction_initial == MV_START_PREVIOUS)
4460         {
4461           if (MovDir[x][y] != MV_NONE)
4462             return;
4463
4464           move_direction_initial = MV_START_AUTOMATIC;
4465         }
4466
4467         if (move_direction_initial == MV_START_RANDOM)
4468           MovDir[x][y] = 1 << RND(4);
4469         else if (move_direction_initial & MV_ANY_DIRECTION)
4470           MovDir[x][y] = move_direction_initial;
4471         else if (move_pattern == MV_ALL_DIRECTIONS ||
4472                  move_pattern == MV_TURNING_LEFT ||
4473                  move_pattern == MV_TURNING_RIGHT ||
4474                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4475                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4476                  move_pattern == MV_TURNING_RANDOM)
4477           MovDir[x][y] = 1 << RND(4);
4478         else if (move_pattern == MV_HORIZONTAL)
4479           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4480         else if (move_pattern == MV_VERTICAL)
4481           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4482         else if (move_pattern & MV_ANY_DIRECTION)
4483           MovDir[x][y] = element_info[element].move_pattern;
4484         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4485                  move_pattern == MV_ALONG_RIGHT_SIDE)
4486         {
4487           /* use random direction as default start direction */
4488           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4489             MovDir[x][y] = 1 << RND(4);
4490
4491           for (i = 0; i < NUM_DIRECTIONS; i++)
4492           {
4493             int x1 = x + xy[i][0];
4494             int y1 = y + xy[i][1];
4495
4496             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4497             {
4498               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4499                 MovDir[x][y] = direction[0][i];
4500               else
4501                 MovDir[x][y] = direction[1][i];
4502
4503               break;
4504             }
4505           }
4506         }                
4507       }
4508       else
4509       {
4510         MovDir[x][y] = 1 << RND(4);
4511
4512         if (element != EL_BUG &&
4513             element != EL_SPACESHIP &&
4514             element != EL_BD_BUTTERFLY &&
4515             element != EL_BD_FIREFLY)
4516           break;
4517
4518         for (i = 0; i < NUM_DIRECTIONS; i++)
4519         {
4520           int x1 = x + xy[i][0];
4521           int y1 = y + xy[i][1];
4522
4523           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4524           {
4525             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4526             {
4527               MovDir[x][y] = direction[0][i];
4528               break;
4529             }
4530             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4531                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4532             {
4533               MovDir[x][y] = direction[1][i];
4534               break;
4535             }
4536           }
4537         }
4538       }
4539       break;
4540   }
4541
4542   GfxDir[x][y] = MovDir[x][y];
4543 }
4544
4545 void InitAmoebaNr(int x, int y)
4546 {
4547   int i;
4548   int group_nr = AmoebeNachbarNr(x, y);
4549
4550   if (group_nr == 0)
4551   {
4552     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4553     {
4554       if (AmoebaCnt[i] == 0)
4555       {
4556         group_nr = i;
4557         break;
4558       }
4559     }
4560   }
4561
4562   AmoebaNr[x][y] = group_nr;
4563   AmoebaCnt[group_nr]++;
4564   AmoebaCnt2[group_nr]++;
4565 }
4566
4567 static void PlayerWins(struct PlayerInfo *player)
4568 {
4569   player->LevelSolved = TRUE;
4570   player->GameOver = TRUE;
4571
4572   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4573                          level.native_em_level->lev->score : player->score);
4574
4575   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4576   player->LevelSolved_CountingScore = player->score_final;
4577 }
4578
4579 void GameWon()
4580 {
4581   static int time, time_final;
4582   static int score, score_final;
4583   static int game_over_delay_1 = 0;
4584   static int game_over_delay_2 = 0;
4585   int game_over_delay_value_1 = 50;
4586   int game_over_delay_value_2 = 50;
4587
4588   if (!local_player->LevelSolved_GameWon)
4589   {
4590     int i;
4591
4592     /* do not start end game actions before the player stops moving (to exit) */
4593     if (local_player->MovPos)
4594       return;
4595
4596     local_player->LevelSolved_GameWon = TRUE;
4597     local_player->LevelSolved_SaveTape = tape.recording;
4598     local_player->LevelSolved_SaveScore = !tape.playing;
4599
4600     if (tape.auto_play)         /* tape might already be stopped here */
4601       tape.auto_play_level_solved = TRUE;
4602
4603 #if 1
4604     TapeStop();
4605 #endif
4606
4607     game_over_delay_1 = game_over_delay_value_1;
4608     game_over_delay_2 = game_over_delay_value_2;
4609
4610     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4611     score = score_final = local_player->score_final;
4612
4613     if (TimeLeft > 0)
4614     {
4615       time_final = 0;
4616       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4617     }
4618     else if (level.time == 0 && TimePlayed < 999)
4619     {
4620       time_final = 999;
4621       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4622     }
4623
4624     local_player->score_final = score_final;
4625
4626     if (level_editor_test_game)
4627     {
4628       time = time_final;
4629       score = score_final;
4630
4631 #if 1
4632       local_player->LevelSolved_CountingTime = time;
4633       local_player->LevelSolved_CountingScore = score;
4634
4635       game_panel_controls[GAME_PANEL_TIME].value = time;
4636       game_panel_controls[GAME_PANEL_SCORE].value = score;
4637
4638       DisplayGameControlValues();
4639 #else
4640       DrawGameValue_Time(time);
4641       DrawGameValue_Score(score);
4642 #endif
4643     }
4644
4645     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4646     {
4647       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4648       {
4649         /* close exit door after last player */
4650         if ((AllPlayersGone &&
4651              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4652               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4653               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4654             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4655             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4656         {
4657           int element = Feld[ExitX][ExitY];
4658
4659 #if 0
4660           if (element == EL_EM_EXIT_OPEN ||
4661               element == EL_EM_STEEL_EXIT_OPEN)
4662           {
4663             Bang(ExitX, ExitY);
4664           }
4665           else
4666 #endif
4667           {
4668             Feld[ExitX][ExitY] =
4669               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4670                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4671                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4672                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4673                EL_EM_STEEL_EXIT_CLOSING);
4674
4675             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4676           }
4677         }
4678
4679         /* player disappears */
4680         DrawLevelField(ExitX, ExitY);
4681       }
4682
4683       for (i = 0; i < MAX_PLAYERS; i++)
4684       {
4685         struct PlayerInfo *player = &stored_player[i];
4686
4687         if (player->present)
4688         {
4689           RemovePlayer(player);
4690
4691           /* player disappears */
4692           DrawLevelField(player->jx, player->jy);
4693         }
4694       }
4695     }
4696
4697     PlaySound(SND_GAME_WINNING);
4698   }
4699
4700   if (game_over_delay_1 > 0)
4701   {
4702     game_over_delay_1--;
4703
4704     return;
4705   }
4706
4707   if (time != time_final)
4708   {
4709     int time_to_go = ABS(time_final - time);
4710     int time_count_dir = (time < time_final ? +1 : -1);
4711     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4712
4713     time  += time_count_steps * time_count_dir;
4714     score += time_count_steps * level.score[SC_TIME_BONUS];
4715
4716 #if 1
4717     local_player->LevelSolved_CountingTime = time;
4718     local_player->LevelSolved_CountingScore = score;
4719
4720     game_panel_controls[GAME_PANEL_TIME].value = time;
4721     game_panel_controls[GAME_PANEL_SCORE].value = score;
4722
4723     DisplayGameControlValues();
4724 #else
4725     DrawGameValue_Time(time);
4726     DrawGameValue_Score(score);
4727 #endif
4728
4729     if (time == time_final)
4730       StopSound(SND_GAME_LEVELTIME_BONUS);
4731     else if (setup.sound_loops)
4732       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4733     else
4734       PlaySound(SND_GAME_LEVELTIME_BONUS);
4735
4736     return;
4737   }
4738
4739   local_player->LevelSolved_PanelOff = TRUE;
4740
4741   if (game_over_delay_2 > 0)
4742   {
4743     game_over_delay_2--;
4744
4745     return;
4746   }
4747
4748 #if 1
4749   GameEnd();
4750 #endif
4751 }
4752
4753 void GameEnd()
4754 {
4755   int hi_pos;
4756   boolean raise_level = FALSE;
4757
4758   local_player->LevelSolved_GameEnd = TRUE;
4759
4760   CloseDoor(DOOR_CLOSE_1);
4761
4762   if (local_player->LevelSolved_SaveTape)
4763   {
4764 #if 0
4765     TapeStop();
4766 #endif
4767
4768 #if 1
4769     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4770 #else
4771     SaveTape(tape.level_nr);            /* ask to save tape */
4772 #endif
4773   }
4774
4775   if (level_editor_test_game)
4776   {
4777     game_status = GAME_MODE_MAIN;
4778
4779 #if 1
4780     DrawAndFadeInMainMenu(REDRAW_FIELD);
4781 #else
4782     DrawMainMenu();
4783 #endif
4784
4785     return;
4786   }
4787
4788   if (!local_player->LevelSolved_SaveScore)
4789   {
4790 #if 1
4791     FadeOut(REDRAW_FIELD);
4792 #endif
4793
4794     game_status = GAME_MODE_MAIN;
4795
4796     DrawAndFadeInMainMenu(REDRAW_FIELD);
4797
4798     return;
4799   }
4800
4801   if (level_nr == leveldir_current->handicap_level)
4802   {
4803     leveldir_current->handicap_level++;
4804     SaveLevelSetup_SeriesInfo();
4805   }
4806
4807   if (level_nr < leveldir_current->last_level)
4808     raise_level = TRUE;                 /* advance to next level */
4809
4810   if ((hi_pos = NewHiScore()) >= 0) 
4811   {
4812     game_status = GAME_MODE_SCORES;
4813
4814     DrawHallOfFame(hi_pos);
4815
4816     if (raise_level)
4817     {
4818       level_nr++;
4819       TapeErase();
4820     }
4821   }
4822   else
4823   {
4824 #if 1
4825     FadeOut(REDRAW_FIELD);
4826 #endif
4827
4828     game_status = GAME_MODE_MAIN;
4829
4830     if (raise_level)
4831     {
4832       level_nr++;
4833       TapeErase();
4834     }
4835
4836     DrawAndFadeInMainMenu(REDRAW_FIELD);
4837   }
4838 }
4839
4840 int NewHiScore()
4841 {
4842   int k, l;
4843   int position = -1;
4844
4845   LoadScore(level_nr);
4846
4847   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4848       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4849     return -1;
4850
4851   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4852   {
4853     if (local_player->score_final > highscore[k].Score)
4854     {
4855       /* player has made it to the hall of fame */
4856
4857       if (k < MAX_SCORE_ENTRIES - 1)
4858       {
4859         int m = MAX_SCORE_ENTRIES - 1;
4860
4861 #ifdef ONE_PER_NAME
4862         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4863           if (strEqual(setup.player_name, highscore[l].Name))
4864             m = l;
4865         if (m == k)     /* player's new highscore overwrites his old one */
4866           goto put_into_list;
4867 #endif
4868
4869         for (l = m; l > k; l--)
4870         {
4871           strcpy(highscore[l].Name, highscore[l - 1].Name);
4872           highscore[l].Score = highscore[l - 1].Score;
4873         }
4874       }
4875
4876 #ifdef ONE_PER_NAME
4877       put_into_list:
4878 #endif
4879       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4880       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4881       highscore[k].Score = local_player->score_final; 
4882       position = k;
4883       break;
4884     }
4885
4886 #ifdef ONE_PER_NAME
4887     else if (!strncmp(setup.player_name, highscore[k].Name,
4888                       MAX_PLAYER_NAME_LEN))
4889       break;    /* player already there with a higher score */
4890 #endif
4891
4892   }
4893
4894   if (position >= 0) 
4895     SaveScore(level_nr);
4896
4897   return position;
4898 }
4899
4900 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4901 {
4902   int element = Feld[x][y];
4903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4905   int horiz_move = (dx != 0);
4906   int sign = (horiz_move ? dx : dy);
4907   int step = sign * element_info[element].move_stepsize;
4908
4909   /* special values for move stepsize for spring and things on conveyor belt */
4910   if (horiz_move)
4911   {
4912     if (CAN_FALL(element) &&
4913         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4914       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4915     else if (element == EL_SPRING)
4916       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4917   }
4918
4919   return step;
4920 }
4921
4922 inline static int getElementMoveStepsize(int x, int y)
4923 {
4924   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4925 }
4926
4927 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4928 {
4929   if (player->GfxAction != action || player->GfxDir != dir)
4930   {
4931 #if 0
4932     printf("Player frame reset! (%d => %d, %d => %d)\n",
4933            player->GfxAction, action, player->GfxDir, dir);
4934 #endif
4935
4936     player->GfxAction = action;
4937     player->GfxDir = dir;
4938     player->Frame = 0;
4939     player->StepFrame = 0;
4940   }
4941 }
4942
4943 #if USE_GFX_RESET_GFX_ANIMATION
4944 static void ResetGfxFrame(int x, int y, boolean redraw)
4945 {
4946   int element = Feld[x][y];
4947   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4948   int last_gfx_frame = GfxFrame[x][y];
4949
4950   if (graphic_info[graphic].anim_global_sync)
4951     GfxFrame[x][y] = FrameCounter;
4952   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4953     GfxFrame[x][y] = CustomValue[x][y];
4954   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4955     GfxFrame[x][y] = element_info[element].collect_score;
4956   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4957     GfxFrame[x][y] = ChangeDelay[x][y];
4958
4959   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4960     DrawLevelGraphicAnimation(x, y, graphic);
4961 }
4962 #endif
4963
4964 static void ResetGfxAnimation(int x, int y)
4965 {
4966   GfxAction[x][y] = ACTION_DEFAULT;
4967   GfxDir[x][y] = MovDir[x][y];
4968   GfxFrame[x][y] = 0;
4969
4970 #if USE_GFX_RESET_GFX_ANIMATION
4971   ResetGfxFrame(x, y, FALSE);
4972 #endif
4973 }
4974
4975 static void ResetRandomAnimationValue(int x, int y)
4976 {
4977   GfxRandom[x][y] = INIT_GFX_RANDOM();
4978 }
4979
4980 void InitMovingField(int x, int y, int direction)
4981 {
4982   int element = Feld[x][y];
4983   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4984   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4985   int newx = x + dx;
4986   int newy = y + dy;
4987   boolean is_moving_before, is_moving_after;
4988 #if 0
4989   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4990 #endif
4991
4992   /* check if element was/is moving or being moved before/after mode change */
4993 #if 1
4994 #if 1
4995   is_moving_before = (WasJustMoving[x][y] != 0);
4996 #else
4997   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4998   is_moving_before = WasJustMoving[x][y];
4999 #endif
5000 #else
5001   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5002 #endif
5003   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5004
5005   /* reset animation only for moving elements which change direction of moving
5006      or which just started or stopped moving
5007      (else CEs with property "can move" / "not moving" are reset each frame) */
5008 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5009 #if 1
5010   if (is_moving_before != is_moving_after ||
5011       direction != MovDir[x][y])
5012     ResetGfxAnimation(x, y);
5013 #else
5014   if ((is_moving_before || is_moving_after) && !continues_moving)
5015     ResetGfxAnimation(x, y);
5016 #endif
5017 #else
5018   if (!continues_moving)
5019     ResetGfxAnimation(x, y);
5020 #endif
5021
5022   MovDir[x][y] = direction;
5023   GfxDir[x][y] = direction;
5024
5025 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5026   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5027                      direction == MV_DOWN && CAN_FALL(element) ?
5028                      ACTION_FALLING : ACTION_MOVING);
5029 #else
5030   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5031                      ACTION_FALLING : ACTION_MOVING);
5032 #endif
5033
5034   /* this is needed for CEs with property "can move" / "not moving" */
5035
5036   if (is_moving_after)
5037   {
5038     if (Feld[newx][newy] == EL_EMPTY)
5039       Feld[newx][newy] = EL_BLOCKED;
5040
5041     MovDir[newx][newy] = MovDir[x][y];
5042
5043 #if USE_NEW_CUSTOM_VALUE
5044     CustomValue[newx][newy] = CustomValue[x][y];
5045 #endif
5046
5047     GfxFrame[newx][newy] = GfxFrame[x][y];
5048     GfxRandom[newx][newy] = GfxRandom[x][y];
5049     GfxAction[newx][newy] = GfxAction[x][y];
5050     GfxDir[newx][newy] = GfxDir[x][y];
5051   }
5052 }
5053
5054 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5055 {
5056   int direction = MovDir[x][y];
5057   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5058   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5059
5060   *goes_to_x = newx;
5061   *goes_to_y = newy;
5062 }
5063
5064 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5065 {
5066   int oldx = x, oldy = y;
5067   int direction = MovDir[x][y];
5068
5069   if (direction == MV_LEFT)
5070     oldx++;
5071   else if (direction == MV_RIGHT)
5072     oldx--;
5073   else if (direction == MV_UP)
5074     oldy++;
5075   else if (direction == MV_DOWN)
5076     oldy--;
5077
5078   *comes_from_x = oldx;
5079   *comes_from_y = oldy;
5080 }
5081
5082 int MovingOrBlocked2Element(int x, int y)
5083 {
5084   int element = Feld[x][y];
5085
5086   if (element == EL_BLOCKED)
5087   {
5088     int oldx, oldy;
5089
5090     Blocked2Moving(x, y, &oldx, &oldy);
5091     return Feld[oldx][oldy];
5092   }
5093   else
5094     return element;
5095 }
5096
5097 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5098 {
5099   /* like MovingOrBlocked2Element(), but if element is moving
5100      and (x,y) is the field the moving element is just leaving,
5101      return EL_BLOCKED instead of the element value */
5102   int element = Feld[x][y];
5103
5104   if (IS_MOVING(x, y))
5105   {
5106     if (element == EL_BLOCKED)
5107     {
5108       int oldx, oldy;
5109
5110       Blocked2Moving(x, y, &oldx, &oldy);
5111       return Feld[oldx][oldy];
5112     }
5113     else
5114       return EL_BLOCKED;
5115   }
5116   else
5117     return element;
5118 }
5119
5120 static void RemoveField(int x, int y)
5121 {
5122   Feld[x][y] = EL_EMPTY;
5123
5124   MovPos[x][y] = 0;
5125   MovDir[x][y] = 0;
5126   MovDelay[x][y] = 0;
5127
5128 #if USE_NEW_CUSTOM_VALUE
5129   CustomValue[x][y] = 0;
5130 #endif
5131
5132   AmoebaNr[x][y] = 0;
5133   ChangeDelay[x][y] = 0;
5134   ChangePage[x][y] = -1;
5135   Pushed[x][y] = FALSE;
5136
5137 #if 0
5138   ExplodeField[x][y] = EX_TYPE_NONE;
5139 #endif
5140
5141   GfxElement[x][y] = EL_UNDEFINED;
5142   GfxAction[x][y] = ACTION_DEFAULT;
5143   GfxDir[x][y] = MV_NONE;
5144 #if 0
5145   /* !!! this would prevent the removed tile from being redrawn !!! */
5146   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5147 #endif
5148 }
5149
5150 void RemoveMovingField(int x, int y)
5151 {
5152   int oldx = x, oldy = y, newx = x, newy = y;
5153   int element = Feld[x][y];
5154   int next_element = EL_UNDEFINED;
5155
5156   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5157     return;
5158
5159   if (IS_MOVING(x, y))
5160   {
5161     Moving2Blocked(x, y, &newx, &newy);
5162
5163     if (Feld[newx][newy] != EL_BLOCKED)
5164     {
5165       /* element is moving, but target field is not free (blocked), but
5166          already occupied by something different (example: acid pool);
5167          in this case, only remove the moving field, but not the target */
5168
5169       RemoveField(oldx, oldy);
5170
5171       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5172
5173       TEST_DrawLevelField(oldx, oldy);
5174
5175       return;
5176     }
5177   }
5178   else if (element == EL_BLOCKED)
5179   {
5180     Blocked2Moving(x, y, &oldx, &oldy);
5181     if (!IS_MOVING(oldx, oldy))
5182       return;
5183   }
5184
5185   if (element == EL_BLOCKED &&
5186       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5187        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5188        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5189        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5190        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5191        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5192     next_element = get_next_element(Feld[oldx][oldy]);
5193
5194   RemoveField(oldx, oldy);
5195   RemoveField(newx, newy);
5196
5197   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5198
5199   if (next_element != EL_UNDEFINED)
5200     Feld[oldx][oldy] = next_element;
5201
5202   TEST_DrawLevelField(oldx, oldy);
5203   TEST_DrawLevelField(newx, newy);
5204 }
5205
5206 void DrawDynamite(int x, int y)
5207 {
5208   int sx = SCREENX(x), sy = SCREENY(y);
5209   int graphic = el2img(Feld[x][y]);
5210   int frame;
5211
5212   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5213     return;
5214
5215   if (IS_WALKABLE_INSIDE(Back[x][y]))
5216     return;
5217
5218   if (Back[x][y])
5219     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5220   else if (Store[x][y])
5221     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5222
5223   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5224
5225   if (Back[x][y] || Store[x][y])
5226     DrawGraphicThruMask(sx, sy, graphic, frame);
5227   else
5228     DrawGraphic(sx, sy, graphic, frame);
5229 }
5230
5231 void CheckDynamite(int x, int y)
5232 {
5233   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5234   {
5235     MovDelay[x][y]--;
5236
5237     if (MovDelay[x][y] != 0)
5238     {
5239       DrawDynamite(x, y);
5240       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5241
5242       return;
5243     }
5244   }
5245
5246   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5247
5248   Bang(x, y);
5249 }
5250
5251 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5252 {
5253   boolean num_checked_players = 0;
5254   int i;
5255
5256   for (i = 0; i < MAX_PLAYERS; i++)
5257   {
5258     if (stored_player[i].active)
5259     {
5260       int sx = stored_player[i].jx;
5261       int sy = stored_player[i].jy;
5262
5263       if (num_checked_players == 0)
5264       {
5265         *sx1 = *sx2 = sx;
5266         *sy1 = *sy2 = sy;
5267       }
5268       else
5269       {
5270         *sx1 = MIN(*sx1, sx);
5271         *sy1 = MIN(*sy1, sy);
5272         *sx2 = MAX(*sx2, sx);
5273         *sy2 = MAX(*sy2, sy);
5274       }
5275
5276       num_checked_players++;
5277     }
5278   }
5279 }
5280
5281 static boolean checkIfAllPlayersFitToScreen_RND()
5282 {
5283   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5284
5285   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5286
5287   return (sx2 - sx1 < SCR_FIELDX &&
5288           sy2 - sy1 < SCR_FIELDY);
5289 }
5290
5291 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5292 {
5293   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5294
5295   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5296
5297   *sx = (sx1 + sx2) / 2;
5298   *sy = (sy1 + sy2) / 2;
5299 }
5300
5301 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5302                         boolean center_screen, boolean quick_relocation)
5303 {
5304   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5305   boolean no_delay = (tape.warp_forward);
5306   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5307   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5308
5309   if (quick_relocation)
5310   {
5311     int offset = game.scroll_delay_value;
5312
5313     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5314     {
5315       if (!level.shifted_relocation || center_screen)
5316       {
5317         /* quick relocation (without scrolling), with centering of screen */
5318
5319         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5320                     x > SBX_Right + MIDPOSX ? SBX_Right :
5321                     x - MIDPOSX);
5322
5323         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5324                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5325                     y - MIDPOSY);
5326       }
5327       else
5328       {
5329         /* quick relocation (without scrolling), but do not center screen */
5330
5331         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5332                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5333                                old_x - MIDPOSX);
5334
5335         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5336                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5337                                old_y - MIDPOSY);
5338
5339         int offset_x = x + (scroll_x - center_scroll_x);
5340         int offset_y = y + (scroll_y - center_scroll_y);
5341
5342         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5343                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5344                     offset_x - MIDPOSX);
5345
5346         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5347                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5348                     offset_y - MIDPOSY);
5349       }
5350     }
5351     else
5352     {
5353       /* quick relocation (without scrolling), inside visible screen area */
5354
5355       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5356           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5357         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5358
5359       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5360           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5361         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5362
5363       /* don't scroll over playfield boundaries */
5364       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5365         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5366
5367       /* don't scroll over playfield boundaries */
5368       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5369         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5370     }
5371
5372     RedrawPlayfield(TRUE, 0,0,0,0);
5373   }
5374   else
5375   {
5376 #if 1
5377     int scroll_xx, scroll_yy;
5378
5379     if (!level.shifted_relocation || center_screen)
5380     {
5381       /* visible relocation (with scrolling), with centering of screen */
5382
5383       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5384                    x > SBX_Right + MIDPOSX ? SBX_Right :
5385                    x - MIDPOSX);
5386
5387       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5388                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5389                    y - MIDPOSY);
5390     }
5391     else
5392     {
5393       /* visible relocation (with scrolling), but do not center screen */
5394
5395       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5396                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5397                              old_x - MIDPOSX);
5398
5399       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5400                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5401                              old_y - MIDPOSY);
5402
5403       int offset_x = x + (scroll_x - center_scroll_x);
5404       int offset_y = y + (scroll_y - center_scroll_y);
5405
5406       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5407                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5408                    offset_x - MIDPOSX);
5409
5410       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5411                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5412                    offset_y - MIDPOSY);
5413     }
5414
5415 #else
5416
5417     /* visible relocation (with scrolling), with centering of screen */
5418
5419     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5420                      x > SBX_Right + MIDPOSX ? SBX_Right :
5421                      x - MIDPOSX);
5422
5423     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5424                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5425                      y - MIDPOSY);
5426 #endif
5427
5428     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5429
5430     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5431     {
5432       int dx = 0, dy = 0;
5433       int fx = FX, fy = FY;
5434
5435       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5436       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5437
5438       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5439         break;
5440
5441       scroll_x -= dx;
5442       scroll_y -= dy;
5443
5444       fx += dx * TILEX / 2;
5445       fy += dy * TILEY / 2;
5446
5447       ScrollLevel(dx, dy);
5448       DrawAllPlayers();
5449
5450       /* scroll in two steps of half tile size to make things smoother */
5451       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5452       FlushDisplay();
5453       Delay(wait_delay_value);
5454
5455       /* scroll second step to align at full tile size */
5456       BackToFront();
5457       Delay(wait_delay_value);
5458     }
5459
5460     DrawAllPlayers();
5461     BackToFront();
5462     Delay(wait_delay_value);
5463   }
5464 }
5465
5466 void RelocatePlayer(int jx, int jy, int el_player_raw)
5467 {
5468   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5469   int player_nr = GET_PLAYER_NR(el_player);
5470   struct PlayerInfo *player = &stored_player[player_nr];
5471   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5472   boolean no_delay = (tape.warp_forward);
5473   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5474   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5475   int old_jx = player->jx;
5476   int old_jy = player->jy;
5477   int old_element = Feld[old_jx][old_jy];
5478   int element = Feld[jx][jy];
5479   boolean player_relocated = (old_jx != jx || old_jy != jy);
5480
5481   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5482   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5483   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5484   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5485   int leave_side_horiz = move_dir_horiz;
5486   int leave_side_vert  = move_dir_vert;
5487   int enter_side = enter_side_horiz | enter_side_vert;
5488   int leave_side = leave_side_horiz | leave_side_vert;
5489
5490   if (player->GameOver)         /* do not reanimate dead player */
5491     return;
5492
5493   if (!player_relocated)        /* no need to relocate the player */
5494     return;
5495
5496   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5497   {
5498     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5499     DrawLevelField(jx, jy);
5500   }
5501
5502   if (player->present)
5503   {
5504     while (player->MovPos)
5505     {
5506       ScrollPlayer(player, SCROLL_GO_ON);
5507       ScrollScreen(NULL, SCROLL_GO_ON);
5508
5509       AdvanceFrameAndPlayerCounters(player->index_nr);
5510
5511       DrawPlayer(player);
5512
5513       BackToFront();
5514       Delay(wait_delay_value);
5515     }
5516
5517     DrawPlayer(player);         /* needed here only to cleanup last field */
5518     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5519
5520     player->is_moving = FALSE;
5521   }
5522
5523   if (IS_CUSTOM_ELEMENT(old_element))
5524     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5525                                CE_LEFT_BY_PLAYER,
5526                                player->index_bit, leave_side);
5527
5528   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5529                                       CE_PLAYER_LEAVES_X,
5530                                       player->index_bit, leave_side);
5531
5532   Feld[jx][jy] = el_player;
5533   InitPlayerField(jx, jy, el_player, TRUE);
5534
5535   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5536   {
5537     Feld[jx][jy] = element;
5538     InitField(jx, jy, FALSE);
5539   }
5540
5541   /* only visually relocate centered player */
5542   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5543                      FALSE, level.instant_relocation);
5544
5545   TestIfPlayerTouchesBadThing(jx, jy);
5546   TestIfPlayerTouchesCustomElement(jx, jy);
5547
5548   if (IS_CUSTOM_ELEMENT(element))
5549     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5550                                player->index_bit, enter_side);
5551
5552   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5553                                       player->index_bit, enter_side);
5554 }
5555
5556 void Explode(int ex, int ey, int phase, int mode)
5557 {
5558   int x, y;
5559   int last_phase;
5560   int border_element;
5561
5562   /* !!! eliminate this variable !!! */
5563   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5564
5565   if (game.explosions_delayed)
5566   {
5567     ExplodeField[ex][ey] = mode;
5568     return;
5569   }
5570
5571   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5572   {
5573     int center_element = Feld[ex][ey];
5574     int artwork_element, explosion_element;     /* set these values later */
5575
5576 #if 0
5577     /* --- This is only really needed (and now handled) in "Impact()". --- */
5578     /* do not explode moving elements that left the explode field in time */
5579     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5580         center_element == EL_EMPTY &&
5581         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5582       return;
5583 #endif
5584
5585 #if 0
5586     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5587     if (mode == EX_TYPE_NORMAL ||
5588         mode == EX_TYPE_CENTER ||
5589         mode == EX_TYPE_CROSS)
5590       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5591 #endif
5592
5593     /* remove things displayed in background while burning dynamite */
5594     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5595       Back[ex][ey] = 0;
5596
5597     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5598     {
5599       /* put moving element to center field (and let it explode there) */
5600       center_element = MovingOrBlocked2Element(ex, ey);
5601       RemoveMovingField(ex, ey);
5602       Feld[ex][ey] = center_element;
5603     }
5604
5605     /* now "center_element" is finally determined -- set related values now */
5606     artwork_element = center_element;           /* for custom player artwork */
5607     explosion_element = center_element;         /* for custom player artwork */
5608
5609     if (IS_PLAYER(ex, ey))
5610     {
5611       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5612
5613       artwork_element = stored_player[player_nr].artwork_element;
5614
5615       if (level.use_explosion_element[player_nr])
5616       {
5617         explosion_element = level.explosion_element[player_nr];
5618         artwork_element = explosion_element;
5619       }
5620     }
5621
5622 #if 1
5623     if (mode == EX_TYPE_NORMAL ||
5624         mode == EX_TYPE_CENTER ||
5625         mode == EX_TYPE_CROSS)
5626       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5627 #endif
5628
5629     last_phase = element_info[explosion_element].explosion_delay + 1;
5630
5631     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5632     {
5633       int xx = x - ex + 1;
5634       int yy = y - ey + 1;
5635       int element;
5636
5637       if (!IN_LEV_FIELD(x, y) ||
5638           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5639           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5640         continue;
5641
5642       element = Feld[x][y];
5643
5644       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5645       {
5646         element = MovingOrBlocked2Element(x, y);
5647
5648         if (!IS_EXPLOSION_PROOF(element))
5649           RemoveMovingField(x, y);
5650       }
5651
5652       /* indestructible elements can only explode in center (but not flames) */
5653       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5654                                            mode == EX_TYPE_BORDER)) ||
5655           element == EL_FLAMES)
5656         continue;
5657
5658       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5659          behaviour, for example when touching a yamyam that explodes to rocks
5660          with active deadly shield, a rock is created under the player !!! */
5661       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5662 #if 0
5663       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5664           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5665            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5666 #else
5667       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5668 #endif
5669       {
5670         if (IS_ACTIVE_BOMB(element))
5671         {
5672           /* re-activate things under the bomb like gate or penguin */
5673           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5674           Back[x][y] = 0;
5675         }
5676
5677         continue;
5678       }
5679
5680       /* save walkable background elements while explosion on same tile */
5681       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5682           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5683         Back[x][y] = element;
5684
5685       /* ignite explodable elements reached by other explosion */
5686       if (element == EL_EXPLOSION)
5687         element = Store2[x][y];
5688
5689       if (AmoebaNr[x][y] &&
5690           (element == EL_AMOEBA_FULL ||
5691            element == EL_BD_AMOEBA ||
5692            element == EL_AMOEBA_GROWING))
5693       {
5694         AmoebaCnt[AmoebaNr[x][y]]--;
5695         AmoebaCnt2[AmoebaNr[x][y]]--;
5696       }
5697
5698       RemoveField(x, y);
5699
5700       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5701       {
5702         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5703
5704         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5705
5706         if (PLAYERINFO(ex, ey)->use_murphy)
5707           Store[x][y] = EL_EMPTY;
5708       }
5709
5710       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5711          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5712       else if (ELEM_IS_PLAYER(center_element))
5713         Store[x][y] = EL_EMPTY;
5714       else if (center_element == EL_YAMYAM)
5715         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5716       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5717         Store[x][y] = element_info[center_element].content.e[xx][yy];
5718 #if 1
5719       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5720          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5721          otherwise) -- FIX THIS !!! */
5722       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5723         Store[x][y] = element_info[element].content.e[1][1];
5724 #else
5725       else if (!CAN_EXPLODE(element))
5726         Store[x][y] = element_info[element].content.e[1][1];
5727 #endif
5728       else
5729         Store[x][y] = EL_EMPTY;
5730
5731       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5732           center_element == EL_AMOEBA_TO_DIAMOND)
5733         Store2[x][y] = element;
5734
5735       Feld[x][y] = EL_EXPLOSION;
5736       GfxElement[x][y] = artwork_element;
5737
5738       ExplodePhase[x][y] = 1;
5739       ExplodeDelay[x][y] = last_phase;
5740
5741       Stop[x][y] = TRUE;
5742     }
5743
5744     if (center_element == EL_YAMYAM)
5745       game.yamyam_content_nr =
5746         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5747
5748     return;
5749   }
5750
5751   if (Stop[ex][ey])
5752     return;
5753
5754   x = ex;
5755   y = ey;
5756
5757   if (phase == 1)
5758     GfxFrame[x][y] = 0;         /* restart explosion animation */
5759
5760   last_phase = ExplodeDelay[x][y];
5761
5762   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5763
5764 #ifdef DEBUG
5765
5766   /* activate this even in non-DEBUG version until cause for crash in
5767      getGraphicAnimationFrame() (see below) is found and eliminated */
5768
5769 #endif
5770 #if 1
5771
5772 #if 1
5773   /* this can happen if the player leaves an explosion just in time */
5774   if (GfxElement[x][y] == EL_UNDEFINED)
5775     GfxElement[x][y] = EL_EMPTY;
5776 #else
5777   if (GfxElement[x][y] == EL_UNDEFINED)
5778   {
5779     printf("\n\n");
5780     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5781     printf("Explode(): This should never happen!\n");
5782     printf("\n\n");
5783
5784     GfxElement[x][y] = EL_EMPTY;
5785   }
5786 #endif
5787
5788 #endif
5789
5790   border_element = Store2[x][y];
5791   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5792     border_element = StorePlayer[x][y];
5793
5794   if (phase == element_info[border_element].ignition_delay ||
5795       phase == last_phase)
5796   {
5797     boolean border_explosion = FALSE;
5798
5799     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5800         !PLAYER_EXPLOSION_PROTECTED(x, y))
5801     {
5802       KillPlayerUnlessExplosionProtected(x, y);
5803       border_explosion = TRUE;
5804     }
5805     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5806     {
5807       Feld[x][y] = Store2[x][y];
5808       Store2[x][y] = 0;
5809       Bang(x, y);
5810       border_explosion = TRUE;
5811     }
5812     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5813     {
5814       AmoebeUmwandeln(x, y);
5815       Store2[x][y] = 0;
5816       border_explosion = TRUE;
5817     }
5818
5819     /* if an element just explodes due to another explosion (chain-reaction),
5820        do not immediately end the new explosion when it was the last frame of
5821        the explosion (as it would be done in the following "if"-statement!) */
5822     if (border_explosion && phase == last_phase)
5823       return;
5824   }
5825
5826   if (phase == last_phase)
5827   {
5828     int element;
5829
5830     element = Feld[x][y] = Store[x][y];
5831     Store[x][y] = Store2[x][y] = 0;
5832     GfxElement[x][y] = EL_UNDEFINED;
5833
5834     /* player can escape from explosions and might therefore be still alive */
5835     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5836         element <= EL_PLAYER_IS_EXPLODING_4)
5837     {
5838       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5839       int explosion_element = EL_PLAYER_1 + player_nr;
5840       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5841       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5842
5843       if (level.use_explosion_element[player_nr])
5844         explosion_element = level.explosion_element[player_nr];
5845
5846       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5847                     element_info[explosion_element].content.e[xx][yy]);
5848     }
5849
5850     /* restore probably existing indestructible background element */
5851     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5852       element = Feld[x][y] = Back[x][y];
5853     Back[x][y] = 0;
5854
5855     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5856     GfxDir[x][y] = MV_NONE;
5857     ChangeDelay[x][y] = 0;
5858     ChangePage[x][y] = -1;
5859
5860 #if USE_NEW_CUSTOM_VALUE
5861     CustomValue[x][y] = 0;
5862 #endif
5863
5864     InitField_WithBug2(x, y, FALSE);
5865
5866     TEST_DrawLevelField(x, y);
5867
5868     TestIfElementTouchesCustomElement(x, y);
5869
5870     if (GFX_CRUMBLED(element))
5871       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5872
5873     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5874       StorePlayer[x][y] = 0;
5875
5876     if (ELEM_IS_PLAYER(element))
5877       RelocatePlayer(x, y, element);
5878   }
5879   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5880   {
5881     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5882     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5883
5884     if (phase == delay)
5885       TEST_DrawLevelFieldCrumbledSand(x, y);
5886
5887     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5888     {
5889       DrawLevelElement(x, y, Back[x][y]);
5890       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5891     }
5892     else if (IS_WALKABLE_UNDER(Back[x][y]))
5893     {
5894       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5895       DrawLevelElementThruMask(x, y, Back[x][y]);
5896     }
5897     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5898       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5899   }
5900 }
5901
5902 void DynaExplode(int ex, int ey)
5903 {
5904   int i, j;
5905   int dynabomb_element = Feld[ex][ey];
5906   int dynabomb_size = 1;
5907   boolean dynabomb_xl = FALSE;
5908   struct PlayerInfo *player;
5909   static int xy[4][2] =
5910   {
5911     { 0, -1 },
5912     { -1, 0 },
5913     { +1, 0 },
5914     { 0, +1 }
5915   };
5916
5917   if (IS_ACTIVE_BOMB(dynabomb_element))
5918   {
5919     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5920     dynabomb_size = player->dynabomb_size;
5921     dynabomb_xl = player->dynabomb_xl;
5922     player->dynabombs_left++;
5923   }
5924
5925   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5926
5927   for (i = 0; i < NUM_DIRECTIONS; i++)
5928   {
5929     for (j = 1; j <= dynabomb_size; j++)
5930     {
5931       int x = ex + j * xy[i][0];
5932       int y = ey + j * xy[i][1];
5933       int element;
5934
5935       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5936         break;
5937
5938       element = Feld[x][y];
5939
5940       /* do not restart explosions of fields with active bombs */
5941       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5942         continue;
5943
5944       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5945
5946       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5947           !IS_DIGGABLE(element) && !dynabomb_xl)
5948         break;
5949     }
5950   }
5951 }
5952
5953 void Bang(int x, int y)
5954 {
5955   int element = MovingOrBlocked2Element(x, y);
5956   int explosion_type = EX_TYPE_NORMAL;
5957
5958   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5959   {
5960     struct PlayerInfo *player = PLAYERINFO(x, y);
5961
5962 #if USE_FIX_CE_ACTION_WITH_PLAYER
5963     element = Feld[x][y] = player->initial_element;
5964 #else
5965     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5966                             player->element_nr);
5967 #endif
5968
5969     if (level.use_explosion_element[player->index_nr])
5970     {
5971       int explosion_element = level.explosion_element[player->index_nr];
5972
5973       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5974         explosion_type = EX_TYPE_CROSS;
5975       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5976         explosion_type = EX_TYPE_CENTER;
5977     }
5978   }
5979
5980   switch (element)
5981   {
5982     case EL_BUG:
5983     case EL_SPACESHIP:
5984     case EL_BD_BUTTERFLY:
5985     case EL_BD_FIREFLY:
5986     case EL_YAMYAM:
5987     case EL_DARK_YAMYAM:
5988     case EL_ROBOT:
5989     case EL_PACMAN:
5990     case EL_MOLE:
5991       RaiseScoreElement(element);
5992       break;
5993
5994     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5995     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5996     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5997     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5998     case EL_DYNABOMB_INCREASE_NUMBER:
5999     case EL_DYNABOMB_INCREASE_SIZE:
6000     case EL_DYNABOMB_INCREASE_POWER:
6001       explosion_type = EX_TYPE_DYNA;
6002       break;
6003
6004     case EL_DC_LANDMINE:
6005 #if 0
6006     case EL_EM_EXIT_OPEN:
6007     case EL_EM_STEEL_EXIT_OPEN:
6008 #endif
6009       explosion_type = EX_TYPE_CENTER;
6010       break;
6011
6012     case EL_PENGUIN:
6013     case EL_LAMP:
6014     case EL_LAMP_ACTIVE:
6015     case EL_AMOEBA_TO_DIAMOND:
6016       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6017         explosion_type = EX_TYPE_CENTER;
6018       break;
6019
6020     default:
6021       if (element_info[element].explosion_type == EXPLODES_CROSS)
6022         explosion_type = EX_TYPE_CROSS;
6023       else if (element_info[element].explosion_type == EXPLODES_1X1)
6024         explosion_type = EX_TYPE_CENTER;
6025       break;
6026   }
6027
6028   if (explosion_type == EX_TYPE_DYNA)
6029     DynaExplode(x, y);
6030   else
6031     Explode(x, y, EX_PHASE_START, explosion_type);
6032
6033   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6034 }
6035
6036 void SplashAcid(int x, int y)
6037 {
6038   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6039       (!IN_LEV_FIELD(x - 1, y - 2) ||
6040        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6041     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6042
6043   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6044       (!IN_LEV_FIELD(x + 1, y - 2) ||
6045        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6046     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6047
6048   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6049 }
6050
6051 static void InitBeltMovement()
6052 {
6053   static int belt_base_element[4] =
6054   {
6055     EL_CONVEYOR_BELT_1_LEFT,
6056     EL_CONVEYOR_BELT_2_LEFT,
6057     EL_CONVEYOR_BELT_3_LEFT,
6058     EL_CONVEYOR_BELT_4_LEFT
6059   };
6060   static int belt_base_active_element[4] =
6061   {
6062     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6063     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6064     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6065     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6066   };
6067
6068   int x, y, i, j;
6069
6070   /* set frame order for belt animation graphic according to belt direction */
6071   for (i = 0; i < NUM_BELTS; i++)
6072   {
6073     int belt_nr = i;
6074
6075     for (j = 0; j < NUM_BELT_PARTS; j++)
6076     {
6077       int element = belt_base_active_element[belt_nr] + j;
6078       int graphic_1 = el2img(element);
6079       int graphic_2 = el2panelimg(element);
6080
6081       if (game.belt_dir[i] == MV_LEFT)
6082       {
6083         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6085       }
6086       else
6087       {
6088         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6089         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6090       }
6091     }
6092   }
6093
6094   SCAN_PLAYFIELD(x, y)
6095   {
6096     int element = Feld[x][y];
6097
6098     for (i = 0; i < NUM_BELTS; i++)
6099     {
6100       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6101       {
6102         int e_belt_nr = getBeltNrFromBeltElement(element);
6103         int belt_nr = i;
6104
6105         if (e_belt_nr == belt_nr)
6106         {
6107           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6108
6109           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6110         }
6111       }
6112     }
6113   }
6114 }
6115
6116 static void ToggleBeltSwitch(int x, int y)
6117 {
6118   static int belt_base_element[4] =
6119   {
6120     EL_CONVEYOR_BELT_1_LEFT,
6121     EL_CONVEYOR_BELT_2_LEFT,
6122     EL_CONVEYOR_BELT_3_LEFT,
6123     EL_CONVEYOR_BELT_4_LEFT
6124   };
6125   static int belt_base_active_element[4] =
6126   {
6127     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6128     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6129     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6130     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6131   };
6132   static int belt_base_switch_element[4] =
6133   {
6134     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6135     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6136     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6137     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6138   };
6139   static int belt_move_dir[4] =
6140   {
6141     MV_LEFT,
6142     MV_NONE,
6143     MV_RIGHT,
6144     MV_NONE,
6145   };
6146
6147   int element = Feld[x][y];
6148   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6149   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6150   int belt_dir = belt_move_dir[belt_dir_nr];
6151   int xx, yy, i;
6152
6153   if (!IS_BELT_SWITCH(element))
6154     return;
6155
6156   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6157   game.belt_dir[belt_nr] = belt_dir;
6158
6159   if (belt_dir_nr == 3)
6160     belt_dir_nr = 1;
6161
6162   /* set frame order for belt animation graphic according to belt direction */
6163   for (i = 0; i < NUM_BELT_PARTS; i++)
6164   {
6165     int element = belt_base_active_element[belt_nr] + i;
6166     int graphic_1 = el2img(element);
6167     int graphic_2 = el2panelimg(element);
6168
6169     if (belt_dir == MV_LEFT)
6170     {
6171       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6172       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6173     }
6174     else
6175     {
6176       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6177       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6178     }
6179   }
6180
6181   SCAN_PLAYFIELD(xx, yy)
6182   {
6183     int element = Feld[xx][yy];
6184
6185     if (IS_BELT_SWITCH(element))
6186     {
6187       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6188
6189       if (e_belt_nr == belt_nr)
6190       {
6191         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6192         TEST_DrawLevelField(xx, yy);
6193       }
6194     }
6195     else if (IS_BELT(element) && belt_dir != MV_NONE)
6196     {
6197       int e_belt_nr = getBeltNrFromBeltElement(element);
6198
6199       if (e_belt_nr == belt_nr)
6200       {
6201         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6202
6203         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6204         TEST_DrawLevelField(xx, yy);
6205       }
6206     }
6207     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6208     {
6209       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6210
6211       if (e_belt_nr == belt_nr)
6212       {
6213         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6214
6215         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6216         TEST_DrawLevelField(xx, yy);
6217       }
6218     }
6219   }
6220 }
6221
6222 static void ToggleSwitchgateSwitch(int x, int y)
6223 {
6224   int xx, yy;
6225
6226   game.switchgate_pos = !game.switchgate_pos;
6227
6228   SCAN_PLAYFIELD(xx, yy)
6229   {
6230     int element = Feld[xx][yy];
6231
6232 #if !USE_BOTH_SWITCHGATE_SWITCHES
6233     if (element == EL_SWITCHGATE_SWITCH_UP ||
6234         element == EL_SWITCHGATE_SWITCH_DOWN)
6235     {
6236       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6237       TEST_DrawLevelField(xx, yy);
6238     }
6239     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6240              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6241     {
6242       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6243       TEST_DrawLevelField(xx, yy);
6244     }
6245 #else
6246     if (element == EL_SWITCHGATE_SWITCH_UP)
6247     {
6248       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6249       TEST_DrawLevelField(xx, yy);
6250     }
6251     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6252     {
6253       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6254       TEST_DrawLevelField(xx, yy);
6255     }
6256     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6257     {
6258       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6259       TEST_DrawLevelField(xx, yy);
6260     }
6261     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6262     {
6263       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6264       TEST_DrawLevelField(xx, yy);
6265     }
6266 #endif
6267     else if (element == EL_SWITCHGATE_OPEN ||
6268              element == EL_SWITCHGATE_OPENING)
6269     {
6270       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6271
6272       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6273     }
6274     else if (element == EL_SWITCHGATE_CLOSED ||
6275              element == EL_SWITCHGATE_CLOSING)
6276     {
6277       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6278
6279       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6280     }
6281   }
6282 }
6283
6284 static int getInvisibleActiveFromInvisibleElement(int element)
6285 {
6286   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6287           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6288           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6289           element);
6290 }
6291
6292 static int getInvisibleFromInvisibleActiveElement(int element)
6293 {
6294   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6295           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6296           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6297           element);
6298 }
6299
6300 static void RedrawAllLightSwitchesAndInvisibleElements()
6301 {
6302   int x, y;
6303
6304   SCAN_PLAYFIELD(x, y)
6305   {
6306     int element = Feld[x][y];
6307
6308     if (element == EL_LIGHT_SWITCH &&
6309         game.light_time_left > 0)
6310     {
6311       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6312       TEST_DrawLevelField(x, y);
6313     }
6314     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6315              game.light_time_left == 0)
6316     {
6317       Feld[x][y] = EL_LIGHT_SWITCH;
6318       TEST_DrawLevelField(x, y);
6319     }
6320     else if (element == EL_EMC_DRIPPER &&
6321              game.light_time_left > 0)
6322     {
6323       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6324       TEST_DrawLevelField(x, y);
6325     }
6326     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6327              game.light_time_left == 0)
6328     {
6329       Feld[x][y] = EL_EMC_DRIPPER;
6330       TEST_DrawLevelField(x, y);
6331     }
6332     else if (element == EL_INVISIBLE_STEELWALL ||
6333              element == EL_INVISIBLE_WALL ||
6334              element == EL_INVISIBLE_SAND)
6335     {
6336       if (game.light_time_left > 0)
6337         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6338
6339       TEST_DrawLevelField(x, y);
6340
6341       /* uncrumble neighbour fields, if needed */
6342       if (element == EL_INVISIBLE_SAND)
6343         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6344     }
6345     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6346              element == EL_INVISIBLE_WALL_ACTIVE ||
6347              element == EL_INVISIBLE_SAND_ACTIVE)
6348     {
6349       if (game.light_time_left == 0)
6350         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6351
6352       TEST_DrawLevelField(x, y);
6353
6354       /* re-crumble neighbour fields, if needed */
6355       if (element == EL_INVISIBLE_SAND)
6356         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6357     }
6358   }
6359 }
6360
6361 static void RedrawAllInvisibleElementsForLenses()
6362 {
6363   int x, y;
6364
6365   SCAN_PLAYFIELD(x, y)
6366   {
6367     int element = Feld[x][y];
6368
6369     if (element == EL_EMC_DRIPPER &&
6370         game.lenses_time_left > 0)
6371     {
6372       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6373       TEST_DrawLevelField(x, y);
6374     }
6375     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6376              game.lenses_time_left == 0)
6377     {
6378       Feld[x][y] = EL_EMC_DRIPPER;
6379       TEST_DrawLevelField(x, y);
6380     }
6381     else if (element == EL_INVISIBLE_STEELWALL ||
6382              element == EL_INVISIBLE_WALL ||
6383              element == EL_INVISIBLE_SAND)
6384     {
6385       if (game.lenses_time_left > 0)
6386         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6387
6388       TEST_DrawLevelField(x, y);
6389
6390       /* uncrumble neighbour fields, if needed */
6391       if (element == EL_INVISIBLE_SAND)
6392         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6393     }
6394     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6395              element == EL_INVISIBLE_WALL_ACTIVE ||
6396              element == EL_INVISIBLE_SAND_ACTIVE)
6397     {
6398       if (game.lenses_time_left == 0)
6399         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6400
6401       TEST_DrawLevelField(x, y);
6402
6403       /* re-crumble neighbour fields, if needed */
6404       if (element == EL_INVISIBLE_SAND)
6405         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6406     }
6407   }
6408 }
6409
6410 static void RedrawAllInvisibleElementsForMagnifier()
6411 {
6412   int x, y;
6413
6414   SCAN_PLAYFIELD(x, y)
6415   {
6416     int element = Feld[x][y];
6417
6418     if (element == EL_EMC_FAKE_GRASS &&
6419         game.magnify_time_left > 0)
6420     {
6421       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6422       TEST_DrawLevelField(x, y);
6423     }
6424     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6425              game.magnify_time_left == 0)
6426     {
6427       Feld[x][y] = EL_EMC_FAKE_GRASS;
6428       TEST_DrawLevelField(x, y);
6429     }
6430     else if (IS_GATE_GRAY(element) &&
6431              game.magnify_time_left > 0)
6432     {
6433       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6434                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6435                     IS_EM_GATE_GRAY(element) ?
6436                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6437                     IS_EMC_GATE_GRAY(element) ?
6438                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6439                     element);
6440       TEST_DrawLevelField(x, y);
6441     }
6442     else if (IS_GATE_GRAY_ACTIVE(element) &&
6443              game.magnify_time_left == 0)
6444     {
6445       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6446                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6447                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6448                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6449                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6450                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6451                     element);
6452       TEST_DrawLevelField(x, y);
6453     }
6454   }
6455 }
6456
6457 static void ToggleLightSwitch(int x, int y)
6458 {
6459   int element = Feld[x][y];
6460
6461   game.light_time_left =
6462     (element == EL_LIGHT_SWITCH ?
6463      level.time_light * FRAMES_PER_SECOND : 0);
6464
6465   RedrawAllLightSwitchesAndInvisibleElements();
6466 }
6467
6468 static void ActivateTimegateSwitch(int x, int y)
6469 {
6470   int xx, yy;
6471
6472   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6473
6474   SCAN_PLAYFIELD(xx, yy)
6475   {
6476     int element = Feld[xx][yy];
6477
6478     if (element == EL_TIMEGATE_CLOSED ||
6479         element == EL_TIMEGATE_CLOSING)
6480     {
6481       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6482       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6483     }
6484
6485     /*
6486     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6487     {
6488       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6489       TEST_DrawLevelField(xx, yy);
6490     }
6491     */
6492
6493   }
6494
6495 #if 1
6496   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6497                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6498 #else
6499   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6500 #endif
6501 }
6502
6503 void Impact(int x, int y)
6504 {
6505   boolean last_line = (y == lev_fieldy - 1);
6506   boolean object_hit = FALSE;
6507   boolean impact = (last_line || object_hit);
6508   int element = Feld[x][y];
6509   int smashed = EL_STEELWALL;
6510
6511   if (!last_line)       /* check if element below was hit */
6512   {
6513     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6514       return;
6515
6516     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6517                                          MovDir[x][y + 1] != MV_DOWN ||
6518                                          MovPos[x][y + 1] <= TILEY / 2));
6519
6520     /* do not smash moving elements that left the smashed field in time */
6521     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6522         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6523       object_hit = FALSE;
6524
6525 #if USE_QUICKSAND_IMPACT_BUGFIX
6526     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6527     {
6528       RemoveMovingField(x, y + 1);
6529       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6530       Feld[x][y + 2] = EL_ROCK;
6531       TEST_DrawLevelField(x, y + 2);
6532
6533       object_hit = TRUE;
6534     }
6535
6536     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6537     {
6538       RemoveMovingField(x, y + 1);
6539       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6540       Feld[x][y + 2] = EL_ROCK;
6541       TEST_DrawLevelField(x, y + 2);
6542
6543       object_hit = TRUE;
6544     }
6545 #endif
6546
6547     if (object_hit)
6548       smashed = MovingOrBlocked2Element(x, y + 1);
6549
6550     impact = (last_line || object_hit);
6551   }
6552
6553   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6554   {
6555     SplashAcid(x, y + 1);
6556     return;
6557   }
6558
6559   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6560   /* only reset graphic animation if graphic really changes after impact */
6561   if (impact &&
6562       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6563   {
6564     ResetGfxAnimation(x, y);
6565     TEST_DrawLevelField(x, y);
6566   }
6567
6568   if (impact && CAN_EXPLODE_IMPACT(element))
6569   {
6570     Bang(x, y);
6571     return;
6572   }
6573   else if (impact && element == EL_PEARL &&
6574            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6575   {
6576     ResetGfxAnimation(x, y);
6577
6578     Feld[x][y] = EL_PEARL_BREAKING;
6579     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6580     return;
6581   }
6582   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6583   {
6584     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6585
6586     return;
6587   }
6588
6589   if (impact && element == EL_AMOEBA_DROP)
6590   {
6591     if (object_hit && IS_PLAYER(x, y + 1))
6592       KillPlayerUnlessEnemyProtected(x, y + 1);
6593     else if (object_hit && smashed == EL_PENGUIN)
6594       Bang(x, y + 1);
6595     else
6596     {
6597       Feld[x][y] = EL_AMOEBA_GROWING;
6598       Store[x][y] = EL_AMOEBA_WET;
6599
6600       ResetRandomAnimationValue(x, y);
6601     }
6602     return;
6603   }
6604
6605   if (object_hit)               /* check which object was hit */
6606   {
6607     if ((CAN_PASS_MAGIC_WALL(element) && 
6608          (smashed == EL_MAGIC_WALL ||
6609           smashed == EL_BD_MAGIC_WALL)) ||
6610         (CAN_PASS_DC_MAGIC_WALL(element) &&
6611          smashed == EL_DC_MAGIC_WALL))
6612     {
6613       int xx, yy;
6614       int activated_magic_wall =
6615         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6616          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6617          EL_DC_MAGIC_WALL_ACTIVE);
6618
6619       /* activate magic wall / mill */
6620       SCAN_PLAYFIELD(xx, yy)
6621       {
6622         if (Feld[xx][yy] == smashed)
6623           Feld[xx][yy] = activated_magic_wall;
6624       }
6625
6626       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6627       game.magic_wall_active = TRUE;
6628
6629       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6630                             SND_MAGIC_WALL_ACTIVATING :
6631                             smashed == EL_BD_MAGIC_WALL ?
6632                             SND_BD_MAGIC_WALL_ACTIVATING :
6633                             SND_DC_MAGIC_WALL_ACTIVATING));
6634     }
6635
6636     if (IS_PLAYER(x, y + 1))
6637     {
6638       if (CAN_SMASH_PLAYER(element))
6639       {
6640         KillPlayerUnlessEnemyProtected(x, y + 1);
6641         return;
6642       }
6643     }
6644     else if (smashed == EL_PENGUIN)
6645     {
6646       if (CAN_SMASH_PLAYER(element))
6647       {
6648         Bang(x, y + 1);
6649         return;
6650       }
6651     }
6652     else if (element == EL_BD_DIAMOND)
6653     {
6654       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6655       {
6656         Bang(x, y + 1);
6657         return;
6658       }
6659     }
6660     else if (((element == EL_SP_INFOTRON ||
6661                element == EL_SP_ZONK) &&
6662               (smashed == EL_SP_SNIKSNAK ||
6663                smashed == EL_SP_ELECTRON ||
6664                smashed == EL_SP_DISK_ORANGE)) ||
6665              (element == EL_SP_INFOTRON &&
6666               smashed == EL_SP_DISK_YELLOW))
6667     {
6668       Bang(x, y + 1);
6669       return;
6670     }
6671     else if (CAN_SMASH_EVERYTHING(element))
6672     {
6673       if (IS_CLASSIC_ENEMY(smashed) ||
6674           CAN_EXPLODE_SMASHED(smashed))
6675       {
6676         Bang(x, y + 1);
6677         return;
6678       }
6679       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6680       {
6681         if (smashed == EL_LAMP ||
6682             smashed == EL_LAMP_ACTIVE)
6683         {
6684           Bang(x, y + 1);
6685           return;
6686         }
6687         else if (smashed == EL_NUT)
6688         {
6689           Feld[x][y + 1] = EL_NUT_BREAKING;
6690           PlayLevelSound(x, y, SND_NUT_BREAKING);
6691           RaiseScoreElement(EL_NUT);
6692           return;
6693         }
6694         else if (smashed == EL_PEARL)
6695         {
6696           ResetGfxAnimation(x, y);
6697
6698           Feld[x][y + 1] = EL_PEARL_BREAKING;
6699           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6700           return;
6701         }
6702         else if (smashed == EL_DIAMOND)
6703         {
6704           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6705           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6706           return;
6707         }
6708         else if (IS_BELT_SWITCH(smashed))
6709         {
6710           ToggleBeltSwitch(x, y + 1);
6711         }
6712         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6713                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6714                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6715                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6716         {
6717           ToggleSwitchgateSwitch(x, y + 1);
6718         }
6719         else if (smashed == EL_LIGHT_SWITCH ||
6720                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6721         {
6722           ToggleLightSwitch(x, y + 1);
6723         }
6724         else
6725         {
6726 #if 0
6727           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6728 #endif
6729
6730           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6731
6732           CheckElementChangeBySide(x, y + 1, smashed, element,
6733                                    CE_SWITCHED, CH_SIDE_TOP);
6734           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6735                                             CH_SIDE_TOP);
6736         }
6737       }
6738       else
6739       {
6740         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6741       }
6742     }
6743   }
6744
6745   /* play sound of magic wall / mill */
6746   if (!last_line &&
6747       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6748        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6749        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6750   {
6751     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6752       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6753     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6754       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6755     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6756       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6757
6758     return;
6759   }
6760
6761   /* play sound of object that hits the ground */
6762   if (last_line || object_hit)
6763     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6764 }
6765
6766 inline static void TurnRoundExt(int x, int y)
6767 {
6768   static struct
6769   {
6770     int dx, dy;
6771   } move_xy[] =
6772   {
6773     {  0,  0 },
6774     { -1,  0 },
6775     { +1,  0 },
6776     {  0,  0 },
6777     {  0, -1 },
6778     {  0,  0 }, { 0, 0 }, { 0, 0 },
6779     {  0, +1 }
6780   };
6781   static struct
6782   {
6783     int left, right, back;
6784   } turn[] =
6785   {
6786     { 0,        0,              0        },
6787     { MV_DOWN,  MV_UP,          MV_RIGHT },
6788     { MV_UP,    MV_DOWN,        MV_LEFT  },
6789     { 0,        0,              0        },
6790     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6791     { 0,        0,              0        },
6792     { 0,        0,              0        },
6793     { 0,        0,              0        },
6794     { MV_RIGHT, MV_LEFT,        MV_UP    }
6795   };
6796
6797   int element = Feld[x][y];
6798   int move_pattern = element_info[element].move_pattern;
6799
6800   int old_move_dir = MovDir[x][y];
6801   int left_dir  = turn[old_move_dir].left;
6802   int right_dir = turn[old_move_dir].right;
6803   int back_dir  = turn[old_move_dir].back;
6804
6805   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6806   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6807   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6808   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6809
6810   int left_x  = x + left_dx,  left_y  = y + left_dy;
6811   int right_x = x + right_dx, right_y = y + right_dy;
6812   int move_x  = x + move_dx,  move_y  = y + move_dy;
6813
6814   int xx, yy;
6815
6816   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6817   {
6818     TestIfBadThingTouchesOtherBadThing(x, y);
6819
6820     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6821       MovDir[x][y] = right_dir;
6822     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6823       MovDir[x][y] = left_dir;
6824
6825     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6826       MovDelay[x][y] = 9;
6827     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6828       MovDelay[x][y] = 1;
6829   }
6830   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6831   {
6832     TestIfBadThingTouchesOtherBadThing(x, y);
6833
6834     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6835       MovDir[x][y] = left_dir;
6836     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6837       MovDir[x][y] = right_dir;
6838
6839     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6840       MovDelay[x][y] = 9;
6841     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6842       MovDelay[x][y] = 1;
6843   }
6844   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6845   {
6846     TestIfBadThingTouchesOtherBadThing(x, y);
6847
6848     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6849       MovDir[x][y] = left_dir;
6850     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6851       MovDir[x][y] = right_dir;
6852
6853     if (MovDir[x][y] != old_move_dir)
6854       MovDelay[x][y] = 9;
6855   }
6856   else if (element == EL_YAMYAM)
6857   {
6858     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6859     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6860
6861     if (can_turn_left && can_turn_right)
6862       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6863     else if (can_turn_left)
6864       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6865     else if (can_turn_right)
6866       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6867     else
6868       MovDir[x][y] = back_dir;
6869
6870     MovDelay[x][y] = 16 + 16 * RND(3);
6871   }
6872   else if (element == EL_DARK_YAMYAM)
6873   {
6874     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6875                                                          left_x, left_y);
6876     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6877                                                          right_x, right_y);
6878
6879     if (can_turn_left && can_turn_right)
6880       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6881     else if (can_turn_left)
6882       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6883     else if (can_turn_right)
6884       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6885     else
6886       MovDir[x][y] = back_dir;
6887
6888     MovDelay[x][y] = 16 + 16 * RND(3);
6889   }
6890   else if (element == EL_PACMAN)
6891   {
6892     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6893     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6894
6895     if (can_turn_left && can_turn_right)
6896       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6897     else if (can_turn_left)
6898       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6899     else if (can_turn_right)
6900       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6901     else
6902       MovDir[x][y] = back_dir;
6903
6904     MovDelay[x][y] = 6 + RND(40);
6905   }
6906   else if (element == EL_PIG)
6907   {
6908     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6909     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6910     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6911     boolean should_turn_left, should_turn_right, should_move_on;
6912     int rnd_value = 24;
6913     int rnd = RND(rnd_value);
6914
6915     should_turn_left = (can_turn_left &&
6916                         (!can_move_on ||
6917                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6918                                                    y + back_dy + left_dy)));
6919     should_turn_right = (can_turn_right &&
6920                          (!can_move_on ||
6921                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6922                                                     y + back_dy + right_dy)));
6923     should_move_on = (can_move_on &&
6924                       (!can_turn_left ||
6925                        !can_turn_right ||
6926                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6927                                                  y + move_dy + left_dy) ||
6928                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6929                                                  y + move_dy + right_dy)));
6930
6931     if (should_turn_left || should_turn_right || should_move_on)
6932     {
6933       if (should_turn_left && should_turn_right && should_move_on)
6934         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6935                         rnd < 2 * rnd_value / 3 ? right_dir :
6936                         old_move_dir);
6937       else if (should_turn_left && should_turn_right)
6938         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6939       else if (should_turn_left && should_move_on)
6940         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6941       else if (should_turn_right && should_move_on)
6942         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6943       else if (should_turn_left)
6944         MovDir[x][y] = left_dir;
6945       else if (should_turn_right)
6946         MovDir[x][y] = right_dir;
6947       else if (should_move_on)
6948         MovDir[x][y] = old_move_dir;
6949     }
6950     else if (can_move_on && rnd > rnd_value / 8)
6951       MovDir[x][y] = old_move_dir;
6952     else if (can_turn_left && can_turn_right)
6953       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6954     else if (can_turn_left && rnd > rnd_value / 8)
6955       MovDir[x][y] = left_dir;
6956     else if (can_turn_right && rnd > rnd_value/8)
6957       MovDir[x][y] = right_dir;
6958     else
6959       MovDir[x][y] = back_dir;
6960
6961     xx = x + move_xy[MovDir[x][y]].dx;
6962     yy = y + move_xy[MovDir[x][y]].dy;
6963
6964     if (!IN_LEV_FIELD(xx, yy) ||
6965         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6966       MovDir[x][y] = old_move_dir;
6967
6968     MovDelay[x][y] = 0;
6969   }
6970   else if (element == EL_DRAGON)
6971   {
6972     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6973     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6974     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6975     int rnd_value = 24;
6976     int rnd = RND(rnd_value);
6977
6978     if (can_move_on && rnd > rnd_value / 8)
6979       MovDir[x][y] = old_move_dir;
6980     else if (can_turn_left && can_turn_right)
6981       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6982     else if (can_turn_left && rnd > rnd_value / 8)
6983       MovDir[x][y] = left_dir;
6984     else if (can_turn_right && rnd > rnd_value / 8)
6985       MovDir[x][y] = right_dir;
6986     else
6987       MovDir[x][y] = back_dir;
6988
6989     xx = x + move_xy[MovDir[x][y]].dx;
6990     yy = y + move_xy[MovDir[x][y]].dy;
6991
6992     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6993       MovDir[x][y] = old_move_dir;
6994
6995     MovDelay[x][y] = 0;
6996   }
6997   else if (element == EL_MOLE)
6998   {
6999     boolean can_move_on =
7000       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7001                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7002                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7003     if (!can_move_on)
7004     {
7005       boolean can_turn_left =
7006         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7007                               IS_AMOEBOID(Feld[left_x][left_y])));
7008
7009       boolean can_turn_right =
7010         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7011                               IS_AMOEBOID(Feld[right_x][right_y])));
7012
7013       if (can_turn_left && can_turn_right)
7014         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7015       else if (can_turn_left)
7016         MovDir[x][y] = left_dir;
7017       else
7018         MovDir[x][y] = right_dir;
7019     }
7020
7021     if (MovDir[x][y] != old_move_dir)
7022       MovDelay[x][y] = 9;
7023   }
7024   else if (element == EL_BALLOON)
7025   {
7026     MovDir[x][y] = game.wind_direction;
7027     MovDelay[x][y] = 0;
7028   }
7029   else if (element == EL_SPRING)
7030   {
7031 #if USE_NEW_SPRING_BUMPER
7032     if (MovDir[x][y] & MV_HORIZONTAL)
7033     {
7034       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7035           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7036       {
7037         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7038         ResetGfxAnimation(move_x, move_y);
7039         TEST_DrawLevelField(move_x, move_y);
7040
7041         MovDir[x][y] = back_dir;
7042       }
7043       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7044                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7045         MovDir[x][y] = MV_NONE;
7046     }
7047 #else
7048     if (MovDir[x][y] & MV_HORIZONTAL &&
7049         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7050          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7051       MovDir[x][y] = MV_NONE;
7052 #endif
7053
7054     MovDelay[x][y] = 0;
7055   }
7056   else if (element == EL_ROBOT ||
7057            element == EL_SATELLITE ||
7058            element == EL_PENGUIN ||
7059            element == EL_EMC_ANDROID)
7060   {
7061     int attr_x = -1, attr_y = -1;
7062
7063     if (AllPlayersGone)
7064     {
7065       attr_x = ExitX;
7066       attr_y = ExitY;
7067     }
7068     else
7069     {
7070       int i;
7071
7072       for (i = 0; i < MAX_PLAYERS; i++)
7073       {
7074         struct PlayerInfo *player = &stored_player[i];
7075         int jx = player->jx, jy = player->jy;
7076
7077         if (!player->active)
7078           continue;
7079
7080         if (attr_x == -1 ||
7081             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7082         {
7083           attr_x = jx;
7084           attr_y = jy;
7085         }
7086       }
7087     }
7088
7089     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7090         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7091          game.engine_version < VERSION_IDENT(3,1,0,0)))
7092     {
7093       attr_x = ZX;
7094       attr_y = ZY;
7095     }
7096
7097     if (element == EL_PENGUIN)
7098     {
7099       int i;
7100       static int xy[4][2] =
7101       {
7102         { 0, -1 },
7103         { -1, 0 },
7104         { +1, 0 },
7105         { 0, +1 }
7106       };
7107
7108       for (i = 0; i < NUM_DIRECTIONS; i++)
7109       {
7110         int ex = x + xy[i][0];
7111         int ey = y + xy[i][1];
7112
7113         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7114                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7115                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7116                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7117         {
7118           attr_x = ex;
7119           attr_y = ey;
7120           break;
7121         }
7122       }
7123     }
7124
7125     MovDir[x][y] = MV_NONE;
7126     if (attr_x < x)
7127       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7128     else if (attr_x > x)
7129       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7130     if (attr_y < y)
7131       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7132     else if (attr_y > y)
7133       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7134
7135     if (element == EL_ROBOT)
7136     {
7137       int newx, newy;
7138
7139       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7140         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7141       Moving2Blocked(x, y, &newx, &newy);
7142
7143       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7144         MovDelay[x][y] = 8 + 8 * !RND(3);
7145       else
7146         MovDelay[x][y] = 16;
7147     }
7148     else if (element == EL_PENGUIN)
7149     {
7150       int newx, newy;
7151
7152       MovDelay[x][y] = 1;
7153
7154       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7155       {
7156         boolean first_horiz = RND(2);
7157         int new_move_dir = MovDir[x][y];
7158
7159         MovDir[x][y] =
7160           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7161         Moving2Blocked(x, y, &newx, &newy);
7162
7163         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7164           return;
7165
7166         MovDir[x][y] =
7167           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7168         Moving2Blocked(x, y, &newx, &newy);
7169
7170         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7171           return;
7172
7173         MovDir[x][y] = old_move_dir;
7174         return;
7175       }
7176     }
7177     else if (element == EL_SATELLITE)
7178     {
7179       int newx, newy;
7180
7181       MovDelay[x][y] = 1;
7182
7183       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7184       {
7185         boolean first_horiz = RND(2);
7186         int new_move_dir = MovDir[x][y];
7187
7188         MovDir[x][y] =
7189           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7190         Moving2Blocked(x, y, &newx, &newy);
7191
7192         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7193           return;
7194
7195         MovDir[x][y] =
7196           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7197         Moving2Blocked(x, y, &newx, &newy);
7198
7199         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7200           return;
7201
7202         MovDir[x][y] = old_move_dir;
7203         return;
7204       }
7205     }
7206     else if (element == EL_EMC_ANDROID)
7207     {
7208       static int check_pos[16] =
7209       {
7210         -1,             /*  0 => (invalid)          */
7211         7,              /*  1 => MV_LEFT            */
7212         3,              /*  2 => MV_RIGHT           */
7213         -1,             /*  3 => (invalid)          */
7214         1,              /*  4 =>            MV_UP   */
7215         0,              /*  5 => MV_LEFT  | MV_UP   */
7216         2,              /*  6 => MV_RIGHT | MV_UP   */
7217         -1,             /*  7 => (invalid)          */
7218         5,              /*  8 =>            MV_DOWN */
7219         6,              /*  9 => MV_LEFT  | MV_DOWN */
7220         4,              /* 10 => MV_RIGHT | MV_DOWN */
7221         -1,             /* 11 => (invalid)          */
7222         -1,             /* 12 => (invalid)          */
7223         -1,             /* 13 => (invalid)          */
7224         -1,             /* 14 => (invalid)          */
7225         -1,             /* 15 => (invalid)          */
7226       };
7227       static struct
7228       {
7229         int dx, dy;
7230         int dir;
7231       } check_xy[8] =
7232       {
7233         { -1, -1,       MV_LEFT  | MV_UP   },
7234         {  0, -1,                  MV_UP   },
7235         { +1, -1,       MV_RIGHT | MV_UP   },
7236         { +1,  0,       MV_RIGHT           },
7237         { +1, +1,       MV_RIGHT | MV_DOWN },
7238         {  0, +1,                  MV_DOWN },
7239         { -1, +1,       MV_LEFT  | MV_DOWN },
7240         { -1,  0,       MV_LEFT            },
7241       };
7242       int start_pos, check_order;
7243       boolean can_clone = FALSE;
7244       int i;
7245
7246       /* check if there is any free field around current position */
7247       for (i = 0; i < 8; i++)
7248       {
7249         int newx = x + check_xy[i].dx;
7250         int newy = y + check_xy[i].dy;
7251
7252         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7253         {
7254           can_clone = TRUE;
7255
7256           break;
7257         }
7258       }
7259
7260       if (can_clone)            /* randomly find an element to clone */
7261       {
7262         can_clone = FALSE;
7263
7264         start_pos = check_pos[RND(8)];
7265         check_order = (RND(2) ? -1 : +1);
7266
7267         for (i = 0; i < 8; i++)
7268         {
7269           int pos_raw = start_pos + i * check_order;
7270           int pos = (pos_raw + 8) % 8;
7271           int newx = x + check_xy[pos].dx;
7272           int newy = y + check_xy[pos].dy;
7273
7274           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7275           {
7276             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7277             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7278
7279             Store[x][y] = Feld[newx][newy];
7280
7281             can_clone = TRUE;
7282
7283             break;
7284           }
7285         }
7286       }
7287
7288       if (can_clone)            /* randomly find a direction to move */
7289       {
7290         can_clone = FALSE;
7291
7292         start_pos = check_pos[RND(8)];
7293         check_order = (RND(2) ? -1 : +1);
7294
7295         for (i = 0; i < 8; i++)
7296         {
7297           int pos_raw = start_pos + i * check_order;
7298           int pos = (pos_raw + 8) % 8;
7299           int newx = x + check_xy[pos].dx;
7300           int newy = y + check_xy[pos].dy;
7301           int new_move_dir = check_xy[pos].dir;
7302
7303           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7304           {
7305             MovDir[x][y] = new_move_dir;
7306             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7307
7308             can_clone = TRUE;
7309
7310             break;
7311           }
7312         }
7313       }
7314
7315       if (can_clone)            /* cloning and moving successful */
7316         return;
7317
7318       /* cannot clone -- try to move towards player */
7319
7320       start_pos = check_pos[MovDir[x][y] & 0x0f];
7321       check_order = (RND(2) ? -1 : +1);
7322
7323       for (i = 0; i < 3; i++)
7324       {
7325         /* first check start_pos, then previous/next or (next/previous) pos */
7326         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7327         int pos = (pos_raw + 8) % 8;
7328         int newx = x + check_xy[pos].dx;
7329         int newy = y + check_xy[pos].dy;
7330         int new_move_dir = check_xy[pos].dir;
7331
7332         if (IS_PLAYER(newx, newy))
7333           break;
7334
7335         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7336         {
7337           MovDir[x][y] = new_move_dir;
7338           MovDelay[x][y] = level.android_move_time * 8 + 1;
7339
7340           break;
7341         }
7342       }
7343     }
7344   }
7345   else if (move_pattern == MV_TURNING_LEFT ||
7346            move_pattern == MV_TURNING_RIGHT ||
7347            move_pattern == MV_TURNING_LEFT_RIGHT ||
7348            move_pattern == MV_TURNING_RIGHT_LEFT ||
7349            move_pattern == MV_TURNING_RANDOM ||
7350            move_pattern == MV_ALL_DIRECTIONS)
7351   {
7352     boolean can_turn_left =
7353       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7354     boolean can_turn_right =
7355       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7356
7357     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7358       return;
7359
7360     if (move_pattern == MV_TURNING_LEFT)
7361       MovDir[x][y] = left_dir;
7362     else if (move_pattern == MV_TURNING_RIGHT)
7363       MovDir[x][y] = right_dir;
7364     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7365       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7366     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7367       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7368     else if (move_pattern == MV_TURNING_RANDOM)
7369       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7370                       can_turn_right && !can_turn_left ? right_dir :
7371                       RND(2) ? left_dir : right_dir);
7372     else if (can_turn_left && can_turn_right)
7373       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7374     else if (can_turn_left)
7375       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7376     else if (can_turn_right)
7377       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7378     else
7379       MovDir[x][y] = back_dir;
7380
7381     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7382   }
7383   else if (move_pattern == MV_HORIZONTAL ||
7384            move_pattern == MV_VERTICAL)
7385   {
7386     if (move_pattern & old_move_dir)
7387       MovDir[x][y] = back_dir;
7388     else if (move_pattern == MV_HORIZONTAL)
7389       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7390     else if (move_pattern == MV_VERTICAL)
7391       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7392
7393     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7394   }
7395   else if (move_pattern & MV_ANY_DIRECTION)
7396   {
7397     MovDir[x][y] = move_pattern;
7398     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399   }
7400   else if (move_pattern & MV_WIND_DIRECTION)
7401   {
7402     MovDir[x][y] = game.wind_direction;
7403     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7404   }
7405   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7406   {
7407     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7408       MovDir[x][y] = left_dir;
7409     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7410       MovDir[x][y] = right_dir;
7411
7412     if (MovDir[x][y] != old_move_dir)
7413       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7414   }
7415   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7416   {
7417     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7418       MovDir[x][y] = right_dir;
7419     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7420       MovDir[x][y] = left_dir;
7421
7422     if (MovDir[x][y] != old_move_dir)
7423       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7424   }
7425   else if (move_pattern == MV_TOWARDS_PLAYER ||
7426            move_pattern == MV_AWAY_FROM_PLAYER)
7427   {
7428     int attr_x = -1, attr_y = -1;
7429     int newx, newy;
7430     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7431
7432     if (AllPlayersGone)
7433     {
7434       attr_x = ExitX;
7435       attr_y = ExitY;
7436     }
7437     else
7438     {
7439       int i;
7440
7441       for (i = 0; i < MAX_PLAYERS; i++)
7442       {
7443         struct PlayerInfo *player = &stored_player[i];
7444         int jx = player->jx, jy = player->jy;
7445
7446         if (!player->active)
7447           continue;
7448
7449         if (attr_x == -1 ||
7450             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7451         {
7452           attr_x = jx;
7453           attr_y = jy;
7454         }
7455       }
7456     }
7457
7458     MovDir[x][y] = MV_NONE;
7459     if (attr_x < x)
7460       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7461     else if (attr_x > x)
7462       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7463     if (attr_y < y)
7464       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7465     else if (attr_y > y)
7466       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7467
7468     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7469
7470     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7471     {
7472       boolean first_horiz = RND(2);
7473       int new_move_dir = MovDir[x][y];
7474
7475       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7476       {
7477         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7478         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7479
7480         return;
7481       }
7482
7483       MovDir[x][y] =
7484         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485       Moving2Blocked(x, y, &newx, &newy);
7486
7487       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7488         return;
7489
7490       MovDir[x][y] =
7491         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492       Moving2Blocked(x, y, &newx, &newy);
7493
7494       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7495         return;
7496
7497       MovDir[x][y] = old_move_dir;
7498     }
7499   }
7500   else if (move_pattern == MV_WHEN_PUSHED ||
7501            move_pattern == MV_WHEN_DROPPED)
7502   {
7503     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7504       MovDir[x][y] = MV_NONE;
7505
7506     MovDelay[x][y] = 0;
7507   }
7508   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7509   {
7510     static int test_xy[7][2] =
7511     {
7512       { 0, -1 },
7513       { -1, 0 },
7514       { +1, 0 },
7515       { 0, +1 },
7516       { 0, -1 },
7517       { -1, 0 },
7518       { +1, 0 },
7519     };
7520     static int test_dir[7] =
7521     {
7522       MV_UP,
7523       MV_LEFT,
7524       MV_RIGHT,
7525       MV_DOWN,
7526       MV_UP,
7527       MV_LEFT,
7528       MV_RIGHT,
7529     };
7530     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7531     int move_preference = -1000000;     /* start with very low preference */
7532     int new_move_dir = MV_NONE;
7533     int start_test = RND(4);
7534     int i;
7535
7536     for (i = 0; i < NUM_DIRECTIONS; i++)
7537     {
7538       int move_dir = test_dir[start_test + i];
7539       int move_dir_preference;
7540
7541       xx = x + test_xy[start_test + i][0];
7542       yy = y + test_xy[start_test + i][1];
7543
7544       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7545           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7546       {
7547         new_move_dir = move_dir;
7548
7549         break;
7550       }
7551
7552       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7553         continue;
7554
7555       move_dir_preference = -1 * RunnerVisit[xx][yy];
7556       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7557         move_dir_preference = PlayerVisit[xx][yy];
7558
7559       if (move_dir_preference > move_preference)
7560       {
7561         /* prefer field that has not been visited for the longest time */
7562         move_preference = move_dir_preference;
7563         new_move_dir = move_dir;
7564       }
7565       else if (move_dir_preference == move_preference &&
7566                move_dir == old_move_dir)
7567       {
7568         /* prefer last direction when all directions are preferred equally */
7569         move_preference = move_dir_preference;
7570         new_move_dir = move_dir;
7571       }
7572     }
7573
7574     MovDir[x][y] = new_move_dir;
7575     if (old_move_dir != new_move_dir)
7576       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7577   }
7578 }
7579
7580 static void TurnRound(int x, int y)
7581 {
7582   int direction = MovDir[x][y];
7583
7584   TurnRoundExt(x, y);
7585
7586   GfxDir[x][y] = MovDir[x][y];
7587
7588   if (direction != MovDir[x][y])
7589     GfxFrame[x][y] = 0;
7590
7591   if (MovDelay[x][y])
7592     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7593
7594   ResetGfxFrame(x, y, FALSE);
7595 }
7596
7597 static boolean JustBeingPushed(int x, int y)
7598 {
7599   int i;
7600
7601   for (i = 0; i < MAX_PLAYERS; i++)
7602   {
7603     struct PlayerInfo *player = &stored_player[i];
7604
7605     if (player->active && player->is_pushing && player->MovPos)
7606     {
7607       int next_jx = player->jx + (player->jx - player->last_jx);
7608       int next_jy = player->jy + (player->jy - player->last_jy);
7609
7610       if (x == next_jx && y == next_jy)
7611         return TRUE;
7612     }
7613   }
7614
7615   return FALSE;
7616 }
7617
7618 void StartMoving(int x, int y)
7619 {
7620   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7621   int element = Feld[x][y];
7622
7623   if (Stop[x][y])
7624     return;
7625
7626   if (MovDelay[x][y] == 0)
7627     GfxAction[x][y] = ACTION_DEFAULT;
7628
7629   if (CAN_FALL(element) && y < lev_fieldy - 1)
7630   {
7631     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7632         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7633       if (JustBeingPushed(x, y))
7634         return;
7635
7636     if (element == EL_QUICKSAND_FULL)
7637     {
7638       if (IS_FREE(x, y + 1))
7639       {
7640         InitMovingField(x, y, MV_DOWN);
7641         started_moving = TRUE;
7642
7643         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7644 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7645         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7646           Store[x][y] = EL_ROCK;
7647 #else
7648         Store[x][y] = EL_ROCK;
7649 #endif
7650
7651         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7652       }
7653       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7654       {
7655         if (!MovDelay[x][y])
7656         {
7657           MovDelay[x][y] = TILEY + 1;
7658
7659           ResetGfxAnimation(x, y);
7660           ResetGfxAnimation(x, y + 1);
7661         }
7662
7663         if (MovDelay[x][y])
7664         {
7665           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7666           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7667
7668           MovDelay[x][y]--;
7669           if (MovDelay[x][y])
7670             return;
7671         }
7672
7673         Feld[x][y] = EL_QUICKSAND_EMPTY;
7674         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7675         Store[x][y + 1] = Store[x][y];
7676         Store[x][y] = 0;
7677
7678         PlayLevelSoundAction(x, y, ACTION_FILLING);
7679       }
7680       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7681       {
7682         if (!MovDelay[x][y])
7683         {
7684           MovDelay[x][y] = TILEY + 1;
7685
7686           ResetGfxAnimation(x, y);
7687           ResetGfxAnimation(x, y + 1);
7688         }
7689
7690         if (MovDelay[x][y])
7691         {
7692           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7693           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7694
7695           MovDelay[x][y]--;
7696           if (MovDelay[x][y])
7697             return;
7698         }
7699
7700         Feld[x][y] = EL_QUICKSAND_EMPTY;
7701         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7702         Store[x][y + 1] = Store[x][y];
7703         Store[x][y] = 0;
7704
7705         PlayLevelSoundAction(x, y, ACTION_FILLING);
7706       }
7707     }
7708     else if (element == EL_QUICKSAND_FAST_FULL)
7709     {
7710       if (IS_FREE(x, y + 1))
7711       {
7712         InitMovingField(x, y, MV_DOWN);
7713         started_moving = TRUE;
7714
7715         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7716 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7717         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7718           Store[x][y] = EL_ROCK;
7719 #else
7720         Store[x][y] = EL_ROCK;
7721 #endif
7722
7723         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7724       }
7725       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7726       {
7727         if (!MovDelay[x][y])
7728         {
7729           MovDelay[x][y] = TILEY + 1;
7730
7731           ResetGfxAnimation(x, y);
7732           ResetGfxAnimation(x, y + 1);
7733         }
7734
7735         if (MovDelay[x][y])
7736         {
7737           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7738           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7739
7740           MovDelay[x][y]--;
7741           if (MovDelay[x][y])
7742             return;
7743         }
7744
7745         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7746         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7747         Store[x][y + 1] = Store[x][y];
7748         Store[x][y] = 0;
7749
7750         PlayLevelSoundAction(x, y, ACTION_FILLING);
7751       }
7752       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7753       {
7754         if (!MovDelay[x][y])
7755         {
7756           MovDelay[x][y] = TILEY + 1;
7757
7758           ResetGfxAnimation(x, y);
7759           ResetGfxAnimation(x, y + 1);
7760         }
7761
7762         if (MovDelay[x][y])
7763         {
7764           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7765           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7766
7767           MovDelay[x][y]--;
7768           if (MovDelay[x][y])
7769             return;
7770         }
7771
7772         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7773         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7774         Store[x][y + 1] = Store[x][y];
7775         Store[x][y] = 0;
7776
7777         PlayLevelSoundAction(x, y, ACTION_FILLING);
7778       }
7779     }
7780     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7781              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7782     {
7783       InitMovingField(x, y, MV_DOWN);
7784       started_moving = TRUE;
7785
7786       Feld[x][y] = EL_QUICKSAND_FILLING;
7787       Store[x][y] = element;
7788
7789       PlayLevelSoundAction(x, y, ACTION_FILLING);
7790     }
7791     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7792              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7793     {
7794       InitMovingField(x, y, MV_DOWN);
7795       started_moving = TRUE;
7796
7797       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7798       Store[x][y] = element;
7799
7800       PlayLevelSoundAction(x, y, ACTION_FILLING);
7801     }
7802     else if (element == EL_MAGIC_WALL_FULL)
7803     {
7804       if (IS_FREE(x, y + 1))
7805       {
7806         InitMovingField(x, y, MV_DOWN);
7807         started_moving = TRUE;
7808
7809         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7810         Store[x][y] = EL_CHANGED(Store[x][y]);
7811       }
7812       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7813       {
7814         if (!MovDelay[x][y])
7815           MovDelay[x][y] = TILEY/4 + 1;
7816
7817         if (MovDelay[x][y])
7818         {
7819           MovDelay[x][y]--;
7820           if (MovDelay[x][y])
7821             return;
7822         }
7823
7824         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7825         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7826         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7827         Store[x][y] = 0;
7828       }
7829     }
7830     else if (element == EL_BD_MAGIC_WALL_FULL)
7831     {
7832       if (IS_FREE(x, y + 1))
7833       {
7834         InitMovingField(x, y, MV_DOWN);
7835         started_moving = TRUE;
7836
7837         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7838         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7839       }
7840       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7841       {
7842         if (!MovDelay[x][y])
7843           MovDelay[x][y] = TILEY/4 + 1;
7844
7845         if (MovDelay[x][y])
7846         {
7847           MovDelay[x][y]--;
7848           if (MovDelay[x][y])
7849             return;
7850         }
7851
7852         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7853         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7854         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7855         Store[x][y] = 0;
7856       }
7857     }
7858     else if (element == EL_DC_MAGIC_WALL_FULL)
7859     {
7860       if (IS_FREE(x, y + 1))
7861       {
7862         InitMovingField(x, y, MV_DOWN);
7863         started_moving = TRUE;
7864
7865         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7866         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7867       }
7868       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7869       {
7870         if (!MovDelay[x][y])
7871           MovDelay[x][y] = TILEY/4 + 1;
7872
7873         if (MovDelay[x][y])
7874         {
7875           MovDelay[x][y]--;
7876           if (MovDelay[x][y])
7877             return;
7878         }
7879
7880         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7881         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7882         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7883         Store[x][y] = 0;
7884       }
7885     }
7886     else if ((CAN_PASS_MAGIC_WALL(element) &&
7887               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7888                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7889              (CAN_PASS_DC_MAGIC_WALL(element) &&
7890               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7891
7892     {
7893       InitMovingField(x, y, MV_DOWN);
7894       started_moving = TRUE;
7895
7896       Feld[x][y] =
7897         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7898          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7899          EL_DC_MAGIC_WALL_FILLING);
7900       Store[x][y] = element;
7901     }
7902     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7903     {
7904       SplashAcid(x, y + 1);
7905
7906       InitMovingField(x, y, MV_DOWN);
7907       started_moving = TRUE;
7908
7909       Store[x][y] = EL_ACID;
7910     }
7911     else if (
7912 #if USE_FIX_IMPACT_COLLISION
7913              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7914               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7915 #else
7916              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7917               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7918 #endif
7919              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7920               CAN_FALL(element) && WasJustFalling[x][y] &&
7921               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7922
7923              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7924               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7925               (Feld[x][y + 1] == EL_BLOCKED)))
7926     {
7927       /* this is needed for a special case not covered by calling "Impact()"
7928          from "ContinueMoving()": if an element moves to a tile directly below
7929          another element which was just falling on that tile (which was empty
7930          in the previous frame), the falling element above would just stop
7931          instead of smashing the element below (in previous version, the above
7932          element was just checked for "moving" instead of "falling", resulting
7933          in incorrect smashes caused by horizontal movement of the above
7934          element; also, the case of the player being the element to smash was
7935          simply not covered here... :-/ ) */
7936
7937       CheckCollision[x][y] = 0;
7938       CheckImpact[x][y] = 0;
7939
7940       Impact(x, y);
7941     }
7942     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7943     {
7944       if (MovDir[x][y] == MV_NONE)
7945       {
7946         InitMovingField(x, y, MV_DOWN);
7947         started_moving = TRUE;
7948       }
7949     }
7950     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7951     {
7952       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7953         MovDir[x][y] = MV_DOWN;
7954
7955       InitMovingField(x, y, MV_DOWN);
7956       started_moving = TRUE;
7957     }
7958     else if (element == EL_AMOEBA_DROP)
7959     {
7960       Feld[x][y] = EL_AMOEBA_GROWING;
7961       Store[x][y] = EL_AMOEBA_WET;
7962     }
7963     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7964               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7965              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7966              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7967     {
7968       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7969                                 (IS_FREE(x - 1, y + 1) ||
7970                                  Feld[x - 1][y + 1] == EL_ACID));
7971       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7972                                 (IS_FREE(x + 1, y + 1) ||
7973                                  Feld[x + 1][y + 1] == EL_ACID));
7974       boolean can_fall_any  = (can_fall_left || can_fall_right);
7975       boolean can_fall_both = (can_fall_left && can_fall_right);
7976       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7977
7978 #if USE_NEW_ALL_SLIPPERY
7979       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7980       {
7981         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7984           can_fall_left = FALSE;
7985         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7988           can_fall_left = FALSE;
7989
7990         can_fall_any  = (can_fall_left || can_fall_right);
7991         can_fall_both = FALSE;
7992       }
7993 #else
7994       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7995       {
7996         if (slippery_type == SLIPPERY_ONLY_LEFT)
7997           can_fall_right = FALSE;
7998         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7999           can_fall_left = FALSE;
8000         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8001           can_fall_right = FALSE;
8002         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8003           can_fall_left = FALSE;
8004
8005         can_fall_any  = (can_fall_left || can_fall_right);
8006         can_fall_both = (can_fall_left && can_fall_right);
8007       }
8008 #endif
8009
8010 #if USE_NEW_ALL_SLIPPERY
8011 #else
8012 #if USE_NEW_SP_SLIPPERY
8013       /* !!! better use the same properties as for custom elements here !!! */
8014       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8015                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8016       {
8017         can_fall_right = FALSE;         /* slip down on left side */
8018         can_fall_both = FALSE;
8019       }
8020 #endif
8021 #endif
8022
8023 #if USE_NEW_ALL_SLIPPERY
8024       if (can_fall_both)
8025       {
8026         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8027           can_fall_right = FALSE;       /* slip down on left side */
8028         else
8029           can_fall_left = !(can_fall_right = RND(2));
8030
8031         can_fall_both = FALSE;
8032       }
8033 #else
8034       if (can_fall_both)
8035       {
8036         if (game.emulation == EMU_BOULDERDASH ||
8037             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8038           can_fall_right = FALSE;       /* slip down on left side */
8039         else
8040           can_fall_left = !(can_fall_right = RND(2));
8041
8042         can_fall_both = FALSE;
8043       }
8044 #endif
8045
8046       if (can_fall_any)
8047       {
8048         /* if not determined otherwise, prefer left side for slipping down */
8049         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8050         started_moving = TRUE;
8051       }
8052     }
8053 #if 0
8054     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8055 #else
8056     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8057 #endif
8058     {
8059       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8060       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8061       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8062       int belt_dir = game.belt_dir[belt_nr];
8063
8064       if ((belt_dir == MV_LEFT  && left_is_free) ||
8065           (belt_dir == MV_RIGHT && right_is_free))
8066       {
8067         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8068
8069         InitMovingField(x, y, belt_dir);
8070         started_moving = TRUE;
8071
8072         Pushed[x][y] = TRUE;
8073         Pushed[nextx][y] = TRUE;
8074
8075         GfxAction[x][y] = ACTION_DEFAULT;
8076       }
8077       else
8078       {
8079         MovDir[x][y] = 0;       /* if element was moving, stop it */
8080       }
8081     }
8082   }
8083
8084   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8085 #if 0
8086   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8087 #else
8088   if (CAN_MOVE(element) && !started_moving)
8089 #endif
8090   {
8091     int move_pattern = element_info[element].move_pattern;
8092     int newx, newy;
8093
8094 #if 0
8095 #if DEBUG
8096     if (MovDir[x][y] == MV_NONE)
8097     {
8098       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8099              x, y, element, element_info[element].token_name);
8100       printf("StartMoving(): This should never happen!\n");
8101     }
8102 #endif
8103 #endif
8104
8105     Moving2Blocked(x, y, &newx, &newy);
8106
8107     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8108       return;
8109
8110     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8111         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8112     {
8113       WasJustMoving[x][y] = 0;
8114       CheckCollision[x][y] = 0;
8115
8116       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8117
8118       if (Feld[x][y] != element)        /* element has changed */
8119         return;
8120     }
8121
8122     if (!MovDelay[x][y])        /* start new movement phase */
8123     {
8124       /* all objects that can change their move direction after each step
8125          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8126
8127       if (element != EL_YAMYAM &&
8128           element != EL_DARK_YAMYAM &&
8129           element != EL_PACMAN &&
8130           !(move_pattern & MV_ANY_DIRECTION) &&
8131           move_pattern != MV_TURNING_LEFT &&
8132           move_pattern != MV_TURNING_RIGHT &&
8133           move_pattern != MV_TURNING_LEFT_RIGHT &&
8134           move_pattern != MV_TURNING_RIGHT_LEFT &&
8135           move_pattern != MV_TURNING_RANDOM)
8136       {
8137         TurnRound(x, y);
8138
8139         if (MovDelay[x][y] && (element == EL_BUG ||
8140                                element == EL_SPACESHIP ||
8141                                element == EL_SP_SNIKSNAK ||
8142                                element == EL_SP_ELECTRON ||
8143                                element == EL_MOLE))
8144           TEST_DrawLevelField(x, y);
8145       }
8146     }
8147
8148     if (MovDelay[x][y])         /* wait some time before next movement */
8149     {
8150       MovDelay[x][y]--;
8151
8152       if (element == EL_ROBOT ||
8153           element == EL_YAMYAM ||
8154           element == EL_DARK_YAMYAM)
8155       {
8156         DrawLevelElementAnimationIfNeeded(x, y, element);
8157         PlayLevelSoundAction(x, y, ACTION_WAITING);
8158       }
8159       else if (element == EL_SP_ELECTRON)
8160         DrawLevelElementAnimationIfNeeded(x, y, element);
8161       else if (element == EL_DRAGON)
8162       {
8163         int i;
8164         int dir = MovDir[x][y];
8165         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8166         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8167         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8168                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8169                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8170                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8171         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8172
8173         GfxAction[x][y] = ACTION_ATTACKING;
8174
8175         if (IS_PLAYER(x, y))
8176           DrawPlayerField(x, y);
8177         else
8178           TEST_DrawLevelField(x, y);
8179
8180         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8181
8182         for (i = 1; i <= 3; i++)
8183         {
8184           int xx = x + i * dx;
8185           int yy = y + i * dy;
8186           int sx = SCREENX(xx);
8187           int sy = SCREENY(yy);
8188           int flame_graphic = graphic + (i - 1);
8189
8190           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8191             break;
8192
8193           if (MovDelay[x][y])
8194           {
8195             int flamed = MovingOrBlocked2Element(xx, yy);
8196
8197             /* !!! */
8198 #if 0
8199             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8200               Bang(xx, yy);
8201             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8202               RemoveMovingField(xx, yy);
8203             else
8204               RemoveField(xx, yy);
8205 #else
8206             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8207               Bang(xx, yy);
8208             else
8209               RemoveMovingField(xx, yy);
8210 #endif
8211
8212             ChangeDelay[xx][yy] = 0;
8213
8214             Feld[xx][yy] = EL_FLAMES;
8215
8216             if (IN_SCR_FIELD(sx, sy))
8217             {
8218               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8219               DrawGraphic(sx, sy, flame_graphic, frame);
8220             }
8221           }
8222           else
8223           {
8224             if (Feld[xx][yy] == EL_FLAMES)
8225               Feld[xx][yy] = EL_EMPTY;
8226             TEST_DrawLevelField(xx, yy);
8227           }
8228         }
8229       }
8230
8231       if (MovDelay[x][y])       /* element still has to wait some time */
8232       {
8233         PlayLevelSoundAction(x, y, ACTION_WAITING);
8234
8235         return;
8236       }
8237     }
8238
8239     /* now make next step */
8240
8241     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8242
8243     if (DONT_COLLIDE_WITH(element) &&
8244         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8245         !PLAYER_ENEMY_PROTECTED(newx, newy))
8246     {
8247       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8248
8249       return;
8250     }
8251
8252     else if (CAN_MOVE_INTO_ACID(element) &&
8253              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8254              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8255              (MovDir[x][y] == MV_DOWN ||
8256               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8257     {
8258       SplashAcid(newx, newy);
8259       Store[x][y] = EL_ACID;
8260     }
8261     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8262     {
8263       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8264           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8265           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8266           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8267       {
8268         RemoveField(x, y);
8269         TEST_DrawLevelField(x, y);
8270
8271         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8272         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8273           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8274
8275         local_player->friends_still_needed--;
8276         if (!local_player->friends_still_needed &&
8277             !local_player->GameOver && AllPlayersGone)
8278           PlayerWins(local_player);
8279
8280         return;
8281       }
8282       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8283       {
8284         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8285           TEST_DrawLevelField(newx, newy);
8286         else
8287           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8288       }
8289       else if (!IS_FREE(newx, newy))
8290       {
8291         GfxAction[x][y] = ACTION_WAITING;
8292
8293         if (IS_PLAYER(x, y))
8294           DrawPlayerField(x, y);
8295         else
8296           TEST_DrawLevelField(x, y);
8297
8298         return;
8299       }
8300     }
8301     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8302     {
8303       if (IS_FOOD_PIG(Feld[newx][newy]))
8304       {
8305         if (IS_MOVING(newx, newy))
8306           RemoveMovingField(newx, newy);
8307         else
8308         {
8309           Feld[newx][newy] = EL_EMPTY;
8310           TEST_DrawLevelField(newx, newy);
8311         }
8312
8313         PlayLevelSound(x, y, SND_PIG_DIGGING);
8314       }
8315       else if (!IS_FREE(newx, newy))
8316       {
8317         if (IS_PLAYER(x, y))
8318           DrawPlayerField(x, y);
8319         else
8320           TEST_DrawLevelField(x, y);
8321
8322         return;
8323       }
8324     }
8325     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8326     {
8327       if (Store[x][y] != EL_EMPTY)
8328       {
8329         boolean can_clone = FALSE;
8330         int xx, yy;
8331
8332         /* check if element to clone is still there */
8333         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8334         {
8335           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8336           {
8337             can_clone = TRUE;
8338
8339             break;
8340           }
8341         }
8342
8343         /* cannot clone or target field not free anymore -- do not clone */
8344         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8345           Store[x][y] = EL_EMPTY;
8346       }
8347
8348       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8349       {
8350         if (IS_MV_DIAGONAL(MovDir[x][y]))
8351         {
8352           int diagonal_move_dir = MovDir[x][y];
8353           int stored = Store[x][y];
8354           int change_delay = 8;
8355           int graphic;
8356
8357           /* android is moving diagonally */
8358
8359           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8360
8361           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8362           GfxElement[x][y] = EL_EMC_ANDROID;
8363           GfxAction[x][y] = ACTION_SHRINKING;
8364           GfxDir[x][y] = diagonal_move_dir;
8365           ChangeDelay[x][y] = change_delay;
8366
8367           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8368                                    GfxDir[x][y]);
8369
8370           DrawLevelGraphicAnimation(x, y, graphic);
8371           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8372
8373           if (Feld[newx][newy] == EL_ACID)
8374           {
8375             SplashAcid(newx, newy);
8376
8377             return;
8378           }
8379
8380           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8381
8382           Store[newx][newy] = EL_EMC_ANDROID;
8383           GfxElement[newx][newy] = EL_EMC_ANDROID;
8384           GfxAction[newx][newy] = ACTION_GROWING;
8385           GfxDir[newx][newy] = diagonal_move_dir;
8386           ChangeDelay[newx][newy] = change_delay;
8387
8388           graphic = el_act_dir2img(GfxElement[newx][newy],
8389                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8390
8391           DrawLevelGraphicAnimation(newx, newy, graphic);
8392           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8393
8394           return;
8395         }
8396         else
8397         {
8398           Feld[newx][newy] = EL_EMPTY;
8399           TEST_DrawLevelField(newx, newy);
8400
8401           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8402         }
8403       }
8404       else if (!IS_FREE(newx, newy))
8405       {
8406 #if 0
8407         if (IS_PLAYER(x, y))
8408           DrawPlayerField(x, y);
8409         else
8410           TEST_DrawLevelField(x, y);
8411 #endif
8412
8413         return;
8414       }
8415     }
8416     else if (IS_CUSTOM_ELEMENT(element) &&
8417              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8418     {
8419 #if 1
8420       if (!DigFieldByCE(newx, newy, element))
8421         return;
8422 #else
8423       int new_element = Feld[newx][newy];
8424
8425       if (!IS_FREE(newx, newy))
8426       {
8427         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8428                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8429                       ACTION_BREAKING);
8430
8431         /* no element can dig solid indestructible elements */
8432         if (IS_INDESTRUCTIBLE(new_element) &&
8433             !IS_DIGGABLE(new_element) &&
8434             !IS_COLLECTIBLE(new_element))
8435           return;
8436
8437         if (AmoebaNr[newx][newy] &&
8438             (new_element == EL_AMOEBA_FULL ||
8439              new_element == EL_BD_AMOEBA ||
8440              new_element == EL_AMOEBA_GROWING))
8441         {
8442           AmoebaCnt[AmoebaNr[newx][newy]]--;
8443           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8444         }
8445
8446         if (IS_MOVING(newx, newy))
8447           RemoveMovingField(newx, newy);
8448         else
8449         {
8450           RemoveField(newx, newy);
8451           TEST_DrawLevelField(newx, newy);
8452         }
8453
8454         /* if digged element was about to explode, prevent the explosion */
8455         ExplodeField[newx][newy] = EX_TYPE_NONE;
8456
8457         PlayLevelSoundAction(x, y, action);
8458       }
8459
8460       Store[newx][newy] = EL_EMPTY;
8461
8462 #if 1
8463       /* this makes it possible to leave the removed element again */
8464       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8465         Store[newx][newy] = new_element;
8466 #else
8467       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8468       {
8469         int move_leave_element = element_info[element].move_leave_element;
8470
8471         /* this makes it possible to leave the removed element again */
8472         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8473                              new_element : move_leave_element);
8474       }
8475 #endif
8476
8477 #endif
8478
8479       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8480       {
8481         RunnerVisit[x][y] = FrameCounter;
8482         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8483       }
8484     }
8485     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8486     {
8487       if (!IS_FREE(newx, newy))
8488       {
8489         if (IS_PLAYER(x, y))
8490           DrawPlayerField(x, y);
8491         else
8492           TEST_DrawLevelField(x, y);
8493
8494         return;
8495       }
8496       else
8497       {
8498         boolean wanna_flame = !RND(10);
8499         int dx = newx - x, dy = newy - y;
8500         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8501         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8502         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8503                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8504         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8505                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8506
8507         if ((wanna_flame ||
8508              IS_CLASSIC_ENEMY(element1) ||
8509              IS_CLASSIC_ENEMY(element2)) &&
8510             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8511             element1 != EL_FLAMES && element2 != EL_FLAMES)
8512         {
8513           ResetGfxAnimation(x, y);
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           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8522
8523           MovDelay[x][y] = 50;
8524
8525           /* !!! */
8526 #if 0
8527           RemoveField(newx, newy);
8528 #endif
8529           Feld[newx][newy] = EL_FLAMES;
8530           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8531           {
8532 #if 0
8533             RemoveField(newx1, newy1);
8534 #endif
8535             Feld[newx1][newy1] = EL_FLAMES;
8536           }
8537           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8538           {
8539 #if 0
8540             RemoveField(newx2, newy2);
8541 #endif
8542             Feld[newx2][newy2] = EL_FLAMES;
8543           }
8544
8545           return;
8546         }
8547       }
8548     }
8549     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8550              Feld[newx][newy] == EL_DIAMOND)
8551     {
8552       if (IS_MOVING(newx, newy))
8553         RemoveMovingField(newx, newy);
8554       else
8555       {
8556         Feld[newx][newy] = EL_EMPTY;
8557         TEST_DrawLevelField(newx, newy);
8558       }
8559
8560       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8561     }
8562     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8563              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8564     {
8565       if (AmoebaNr[newx][newy])
8566       {
8567         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8568         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8569             Feld[newx][newy] == EL_BD_AMOEBA)
8570           AmoebaCnt[AmoebaNr[newx][newy]]--;
8571       }
8572
8573 #if 0
8574       /* !!! test !!! */
8575       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8576       {
8577         RemoveMovingField(newx, newy);
8578       }
8579 #else
8580       if (IS_MOVING(newx, newy))
8581       {
8582         RemoveMovingField(newx, newy);
8583       }
8584 #endif
8585       else
8586       {
8587         Feld[newx][newy] = EL_EMPTY;
8588         TEST_DrawLevelField(newx, newy);
8589       }
8590
8591       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8592     }
8593     else if ((element == EL_PACMAN || element == EL_MOLE)
8594              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8595     {
8596       if (AmoebaNr[newx][newy])
8597       {
8598         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8599         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8600             Feld[newx][newy] == EL_BD_AMOEBA)
8601           AmoebaCnt[AmoebaNr[newx][newy]]--;
8602       }
8603
8604       if (element == EL_MOLE)
8605       {
8606         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8607         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8608
8609         ResetGfxAnimation(x, y);
8610         GfxAction[x][y] = ACTION_DIGGING;
8611         TEST_DrawLevelField(x, y);
8612
8613         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8614
8615         return;                         /* wait for shrinking amoeba */
8616       }
8617       else      /* element == EL_PACMAN */
8618       {
8619         Feld[newx][newy] = EL_EMPTY;
8620         TEST_DrawLevelField(newx, newy);
8621         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8622       }
8623     }
8624     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8625              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8626               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8627     {
8628       /* wait for shrinking amoeba to completely disappear */
8629       return;
8630     }
8631     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8632     {
8633       /* object was running against a wall */
8634
8635       TurnRound(x, y);
8636
8637 #if 0
8638       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8639       if (move_pattern & MV_ANY_DIRECTION &&
8640           move_pattern == MovDir[x][y])
8641       {
8642         int blocking_element =
8643           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8644
8645         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8646                                  MovDir[x][y]);
8647
8648         element = Feld[x][y];   /* element might have changed */
8649       }
8650 #endif
8651
8652       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8653         DrawLevelElementAnimation(x, y, element);
8654
8655       if (DONT_TOUCH(element))
8656         TestIfBadThingTouchesPlayer(x, y);
8657
8658       return;
8659     }
8660
8661     InitMovingField(x, y, MovDir[x][y]);
8662
8663     PlayLevelSoundAction(x, y, ACTION_MOVING);
8664   }
8665
8666   if (MovDir[x][y])
8667     ContinueMoving(x, y);
8668 }
8669
8670 void ContinueMoving(int x, int y)
8671 {
8672   int element = Feld[x][y];
8673   struct ElementInfo *ei = &element_info[element];
8674   int direction = MovDir[x][y];
8675   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8676   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8677   int newx = x + dx, newy = y + dy;
8678   int stored = Store[x][y];
8679   int stored_new = Store[newx][newy];
8680   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8681   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8682   boolean last_line = (newy == lev_fieldy - 1);
8683
8684   MovPos[x][y] += getElementMoveStepsize(x, y);
8685
8686   if (pushed_by_player) /* special case: moving object pushed by player */
8687     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8688
8689   if (ABS(MovPos[x][y]) < TILEX)
8690   {
8691 #if 0
8692     int ee = Feld[x][y];
8693     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8694     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8695
8696     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8697            x, y, ABS(MovPos[x][y]),
8698            ee, gg, ff,
8699            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8700 #endif
8701
8702     TEST_DrawLevelField(x, y);
8703
8704     return;     /* element is still moving */
8705   }
8706
8707   /* element reached destination field */
8708
8709   Feld[x][y] = EL_EMPTY;
8710   Feld[newx][newy] = element;
8711   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8712
8713   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8714   {
8715     element = Feld[newx][newy] = EL_ACID;
8716   }
8717   else if (element == EL_MOLE)
8718   {
8719     Feld[x][y] = EL_SAND;
8720
8721     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8722   }
8723   else if (element == EL_QUICKSAND_FILLING)
8724   {
8725     element = Feld[newx][newy] = get_next_element(element);
8726     Store[newx][newy] = Store[x][y];
8727   }
8728   else if (element == EL_QUICKSAND_EMPTYING)
8729   {
8730     Feld[x][y] = get_next_element(element);
8731     element = Feld[newx][newy] = Store[x][y];
8732   }
8733   else if (element == EL_QUICKSAND_FAST_FILLING)
8734   {
8735     element = Feld[newx][newy] = get_next_element(element);
8736     Store[newx][newy] = Store[x][y];
8737   }
8738   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8739   {
8740     Feld[x][y] = get_next_element(element);
8741     element = Feld[newx][newy] = Store[x][y];
8742   }
8743   else if (element == EL_MAGIC_WALL_FILLING)
8744   {
8745     element = Feld[newx][newy] = get_next_element(element);
8746     if (!game.magic_wall_active)
8747       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8748     Store[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_MAGIC_WALL_EMPTYING)
8751   {
8752     Feld[x][y] = get_next_element(element);
8753     if (!game.magic_wall_active)
8754       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8755     element = Feld[newx][newy] = Store[x][y];
8756
8757 #if USE_NEW_CUSTOM_VALUE
8758     InitField(newx, newy, FALSE);
8759 #endif
8760   }
8761   else if (element == EL_BD_MAGIC_WALL_FILLING)
8762   {
8763     element = Feld[newx][newy] = get_next_element(element);
8764     if (!game.magic_wall_active)
8765       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8766     Store[newx][newy] = Store[x][y];
8767   }
8768   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8769   {
8770     Feld[x][y] = get_next_element(element);
8771     if (!game.magic_wall_active)
8772       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8773     element = Feld[newx][newy] = Store[x][y];
8774
8775 #if USE_NEW_CUSTOM_VALUE
8776     InitField(newx, newy, FALSE);
8777 #endif
8778   }
8779   else if (element == EL_DC_MAGIC_WALL_FILLING)
8780   {
8781     element = Feld[newx][newy] = get_next_element(element);
8782     if (!game.magic_wall_active)
8783       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8784     Store[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8787   {
8788     Feld[x][y] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8791     element = Feld[newx][newy] = Store[x][y];
8792
8793 #if USE_NEW_CUSTOM_VALUE
8794     InitField(newx, newy, FALSE);
8795 #endif
8796   }
8797   else if (element == EL_AMOEBA_DROPPING)
8798   {
8799     Feld[x][y] = get_next_element(element);
8800     element = Feld[newx][newy] = Store[x][y];
8801   }
8802   else if (element == EL_SOKOBAN_OBJECT)
8803   {
8804     if (Back[x][y])
8805       Feld[x][y] = Back[x][y];
8806
8807     if (Back[newx][newy])
8808       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8809
8810     Back[x][y] = Back[newx][newy] = 0;
8811   }
8812
8813   Store[x][y] = EL_EMPTY;
8814   MovPos[x][y] = 0;
8815   MovDir[x][y] = 0;
8816   MovDelay[x][y] = 0;
8817
8818   MovDelay[newx][newy] = 0;
8819
8820   if (CAN_CHANGE_OR_HAS_ACTION(element))
8821   {
8822     /* copy element change control values to new field */
8823     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8824     ChangePage[newx][newy]  = ChangePage[x][y];
8825     ChangeCount[newx][newy] = ChangeCount[x][y];
8826     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8827   }
8828
8829 #if USE_NEW_CUSTOM_VALUE
8830   CustomValue[newx][newy] = CustomValue[x][y];
8831 #endif
8832
8833   ChangeDelay[x][y] = 0;
8834   ChangePage[x][y] = -1;
8835   ChangeCount[x][y] = 0;
8836   ChangeEvent[x][y] = -1;
8837
8838 #if USE_NEW_CUSTOM_VALUE
8839   CustomValue[x][y] = 0;
8840 #endif
8841
8842   /* copy animation control values to new field */
8843   GfxFrame[newx][newy]  = GfxFrame[x][y];
8844   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8845   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8846   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8847
8848   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8849
8850   /* some elements can leave other elements behind after moving */
8851 #if 1
8852   if (ei->move_leave_element != EL_EMPTY &&
8853       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8854       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8855 #else
8856   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8857       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8858       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8859 #endif
8860   {
8861     int move_leave_element = ei->move_leave_element;
8862
8863 #if 1
8864 #if 1
8865     /* this makes it possible to leave the removed element again */
8866     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8867       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8868 #else
8869     /* this makes it possible to leave the removed element again */
8870     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8871       move_leave_element = stored;
8872 #endif
8873 #else
8874     /* this makes it possible to leave the removed element again */
8875     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8876         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8877       move_leave_element = stored;
8878 #endif
8879
8880     Feld[x][y] = move_leave_element;
8881
8882     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8883       MovDir[x][y] = direction;
8884
8885     InitField(x, y, FALSE);
8886
8887     if (GFX_CRUMBLED(Feld[x][y]))
8888       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8889
8890     if (ELEM_IS_PLAYER(move_leave_element))
8891       RelocatePlayer(x, y, move_leave_element);
8892   }
8893
8894   /* do this after checking for left-behind element */
8895   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8896
8897   if (!CAN_MOVE(element) ||
8898       (CAN_FALL(element) && direction == MV_DOWN &&
8899        (element == EL_SPRING ||
8900         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8901         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8902     GfxDir[x][y] = MovDir[newx][newy] = 0;
8903
8904   TEST_DrawLevelField(x, y);
8905   TEST_DrawLevelField(newx, newy);
8906
8907   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8908
8909   /* prevent pushed element from moving on in pushed direction */
8910   if (pushed_by_player && CAN_MOVE(element) &&
8911       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8912       !(element_info[element].move_pattern & direction))
8913     TurnRound(newx, newy);
8914
8915   /* prevent elements on conveyor belt from moving on in last direction */
8916   if (pushed_by_conveyor && CAN_FALL(element) &&
8917       direction & MV_HORIZONTAL)
8918     MovDir[newx][newy] = 0;
8919
8920   if (!pushed_by_player)
8921   {
8922     int nextx = newx + dx, nexty = newy + dy;
8923     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8924
8925     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8926
8927     if (CAN_FALL(element) && direction == MV_DOWN)
8928       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8929
8930     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8931       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8932
8933 #if USE_FIX_IMPACT_COLLISION
8934     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8935       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8936 #endif
8937   }
8938
8939   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8940   {
8941     TestIfBadThingTouchesPlayer(newx, newy);
8942     TestIfBadThingTouchesFriend(newx, newy);
8943
8944     if (!IS_CUSTOM_ELEMENT(element))
8945       TestIfBadThingTouchesOtherBadThing(newx, newy);
8946   }
8947   else if (element == EL_PENGUIN)
8948     TestIfFriendTouchesBadThing(newx, newy);
8949
8950   if (DONT_GET_HIT_BY(element))
8951   {
8952     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8953   }
8954
8955   /* give the player one last chance (one more frame) to move away */
8956   if (CAN_FALL(element) && direction == MV_DOWN &&
8957       (last_line || (!IS_FREE(x, newy + 1) &&
8958                      (!IS_PLAYER(x, newy + 1) ||
8959                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8960     Impact(x, newy);
8961
8962   if (pushed_by_player && !game.use_change_when_pushing_bug)
8963   {
8964     int push_side = MV_DIR_OPPOSITE(direction);
8965     struct PlayerInfo *player = PLAYERINFO(x, y);
8966
8967     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8968                                player->index_bit, push_side);
8969     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8970                                         player->index_bit, push_side);
8971   }
8972
8973   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8974     MovDelay[newx][newy] = 1;
8975
8976   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8977
8978   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8979
8980 #if 0
8981   if (ChangePage[newx][newy] != -1)             /* delayed change */
8982   {
8983     int page = ChangePage[newx][newy];
8984     struct ElementChangeInfo *change = &ei->change_page[page];
8985
8986     ChangePage[newx][newy] = -1;
8987
8988     if (change->can_change)
8989     {
8990       if (ChangeElement(newx, newy, element, page))
8991       {
8992         if (change->post_change_function)
8993           change->post_change_function(newx, newy);
8994       }
8995     }
8996
8997     if (change->has_action)
8998       ExecuteCustomElementAction(newx, newy, element, page);
8999   }
9000 #endif
9001
9002   TestIfElementHitsCustomElement(newx, newy, direction);
9003   TestIfPlayerTouchesCustomElement(newx, newy);
9004   TestIfElementTouchesCustomElement(newx, newy);
9005
9006   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9007       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9008     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9009                              MV_DIR_OPPOSITE(direction));
9010 }
9011
9012 int AmoebeNachbarNr(int ax, int ay)
9013 {
9014   int i;
9015   int element = Feld[ax][ay];
9016   int group_nr = 0;
9017   static int xy[4][2] =
9018   {
9019     { 0, -1 },
9020     { -1, 0 },
9021     { +1, 0 },
9022     { 0, +1 }
9023   };
9024
9025   for (i = 0; i < NUM_DIRECTIONS; i++)
9026   {
9027     int x = ax + xy[i][0];
9028     int y = ay + xy[i][1];
9029
9030     if (!IN_LEV_FIELD(x, y))
9031       continue;
9032
9033     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9034       group_nr = AmoebaNr[x][y];
9035   }
9036
9037   return group_nr;
9038 }
9039
9040 void AmoebenVereinigen(int ax, int ay)
9041 {
9042   int i, x, y, xx, yy;
9043   int new_group_nr = AmoebaNr[ax][ay];
9044   static int xy[4][2] =
9045   {
9046     { 0, -1 },
9047     { -1, 0 },
9048     { +1, 0 },
9049     { 0, +1 }
9050   };
9051
9052   if (new_group_nr == 0)
9053     return;
9054
9055   for (i = 0; i < NUM_DIRECTIONS; i++)
9056   {
9057     x = ax + xy[i][0];
9058     y = ay + xy[i][1];
9059
9060     if (!IN_LEV_FIELD(x, y))
9061       continue;
9062
9063     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9064          Feld[x][y] == EL_BD_AMOEBA ||
9065          Feld[x][y] == EL_AMOEBA_DEAD) &&
9066         AmoebaNr[x][y] != new_group_nr)
9067     {
9068       int old_group_nr = AmoebaNr[x][y];
9069
9070       if (old_group_nr == 0)
9071         return;
9072
9073       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9074       AmoebaCnt[old_group_nr] = 0;
9075       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9076       AmoebaCnt2[old_group_nr] = 0;
9077
9078       SCAN_PLAYFIELD(xx, yy)
9079       {
9080         if (AmoebaNr[xx][yy] == old_group_nr)
9081           AmoebaNr[xx][yy] = new_group_nr;
9082       }
9083     }
9084   }
9085 }
9086
9087 void AmoebeUmwandeln(int ax, int ay)
9088 {
9089   int i, x, y;
9090
9091   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9092   {
9093     int group_nr = AmoebaNr[ax][ay];
9094
9095 #ifdef DEBUG
9096     if (group_nr == 0)
9097     {
9098       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9099       printf("AmoebeUmwandeln(): This should never happen!\n");
9100       return;
9101     }
9102 #endif
9103
9104     SCAN_PLAYFIELD(x, y)
9105     {
9106       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9107       {
9108         AmoebaNr[x][y] = 0;
9109         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9110       }
9111     }
9112
9113     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9114                             SND_AMOEBA_TURNING_TO_GEM :
9115                             SND_AMOEBA_TURNING_TO_ROCK));
9116     Bang(ax, ay);
9117   }
9118   else
9119   {
9120     static int xy[4][2] =
9121     {
9122       { 0, -1 },
9123       { -1, 0 },
9124       { +1, 0 },
9125       { 0, +1 }
9126     };
9127
9128     for (i = 0; i < NUM_DIRECTIONS; i++)
9129     {
9130       x = ax + xy[i][0];
9131       y = ay + xy[i][1];
9132
9133       if (!IN_LEV_FIELD(x, y))
9134         continue;
9135
9136       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9137       {
9138         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9139                               SND_AMOEBA_TURNING_TO_GEM :
9140                               SND_AMOEBA_TURNING_TO_ROCK));
9141         Bang(x, y);
9142       }
9143     }
9144   }
9145 }
9146
9147 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9148 {
9149   int x, y;
9150   int group_nr = AmoebaNr[ax][ay];
9151   boolean done = FALSE;
9152
9153 #ifdef DEBUG
9154   if (group_nr == 0)
9155   {
9156     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9157     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9158     return;
9159   }
9160 #endif
9161
9162   SCAN_PLAYFIELD(x, y)
9163   {
9164     if (AmoebaNr[x][y] == group_nr &&
9165         (Feld[x][y] == EL_AMOEBA_DEAD ||
9166          Feld[x][y] == EL_BD_AMOEBA ||
9167          Feld[x][y] == EL_AMOEBA_GROWING))
9168     {
9169       AmoebaNr[x][y] = 0;
9170       Feld[x][y] = new_element;
9171       InitField(x, y, FALSE);
9172       TEST_DrawLevelField(x, y);
9173       done = TRUE;
9174     }
9175   }
9176
9177   if (done)
9178     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9179                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9180                             SND_BD_AMOEBA_TURNING_TO_GEM));
9181 }
9182
9183 void AmoebeWaechst(int x, int y)
9184 {
9185   static unsigned long sound_delay = 0;
9186   static unsigned long sound_delay_value = 0;
9187
9188   if (!MovDelay[x][y])          /* start new growing cycle */
9189   {
9190     MovDelay[x][y] = 7;
9191
9192     if (DelayReached(&sound_delay, sound_delay_value))
9193     {
9194       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9195       sound_delay_value = 30;
9196     }
9197   }
9198
9199   if (MovDelay[x][y])           /* wait some time before growing bigger */
9200   {
9201     MovDelay[x][y]--;
9202     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9203     {
9204       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9205                                            6 - MovDelay[x][y]);
9206
9207       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9208     }
9209
9210     if (!MovDelay[x][y])
9211     {
9212       Feld[x][y] = Store[x][y];
9213       Store[x][y] = 0;
9214       TEST_DrawLevelField(x, y);
9215     }
9216   }
9217 }
9218
9219 void AmoebaDisappearing(int x, int y)
9220 {
9221   static unsigned long sound_delay = 0;
9222   static unsigned long sound_delay_value = 0;
9223
9224   if (!MovDelay[x][y])          /* start new shrinking cycle */
9225   {
9226     MovDelay[x][y] = 7;
9227
9228     if (DelayReached(&sound_delay, sound_delay_value))
9229       sound_delay_value = 30;
9230   }
9231
9232   if (MovDelay[x][y])           /* wait some time before shrinking */
9233   {
9234     MovDelay[x][y]--;
9235     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9236     {
9237       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9238                                            6 - MovDelay[x][y]);
9239
9240       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9241     }
9242
9243     if (!MovDelay[x][y])
9244     {
9245       Feld[x][y] = EL_EMPTY;
9246       TEST_DrawLevelField(x, y);
9247
9248       /* don't let mole enter this field in this cycle;
9249          (give priority to objects falling to this field from above) */
9250       Stop[x][y] = TRUE;
9251     }
9252   }
9253 }
9254
9255 void AmoebeAbleger(int ax, int ay)
9256 {
9257   int i;
9258   int element = Feld[ax][ay];
9259   int graphic = el2img(element);
9260   int newax = ax, neway = ay;
9261   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9262   static int xy[4][2] =
9263   {
9264     { 0, -1 },
9265     { -1, 0 },
9266     { +1, 0 },
9267     { 0, +1 }
9268   };
9269
9270   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9271   {
9272     Feld[ax][ay] = EL_AMOEBA_DEAD;
9273     TEST_DrawLevelField(ax, ay);
9274     return;
9275   }
9276
9277   if (IS_ANIMATED(graphic))
9278     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9279
9280   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9281     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9282
9283   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9284   {
9285     MovDelay[ax][ay]--;
9286     if (MovDelay[ax][ay])
9287       return;
9288   }
9289
9290   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9291   {
9292     int start = RND(4);
9293     int x = ax + xy[start][0];
9294     int y = ay + xy[start][1];
9295
9296     if (!IN_LEV_FIELD(x, y))
9297       return;
9298
9299     if (IS_FREE(x, y) ||
9300         CAN_GROW_INTO(Feld[x][y]) ||
9301         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9302         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9303     {
9304       newax = x;
9305       neway = y;
9306     }
9307
9308     if (newax == ax && neway == ay)
9309       return;
9310   }
9311   else                          /* normal or "filled" (BD style) amoeba */
9312   {
9313     int start = RND(4);
9314     boolean waiting_for_player = FALSE;
9315
9316     for (i = 0; i < NUM_DIRECTIONS; i++)
9317     {
9318       int j = (start + i) % 4;
9319       int x = ax + xy[j][0];
9320       int y = ay + xy[j][1];
9321
9322       if (!IN_LEV_FIELD(x, y))
9323         continue;
9324
9325       if (IS_FREE(x, y) ||
9326           CAN_GROW_INTO(Feld[x][y]) ||
9327           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9328           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9329       {
9330         newax = x;
9331         neway = y;
9332         break;
9333       }
9334       else if (IS_PLAYER(x, y))
9335         waiting_for_player = TRUE;
9336     }
9337
9338     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9339     {
9340       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9341       {
9342         Feld[ax][ay] = EL_AMOEBA_DEAD;
9343         TEST_DrawLevelField(ax, ay);
9344         AmoebaCnt[AmoebaNr[ax][ay]]--;
9345
9346         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9347         {
9348           if (element == EL_AMOEBA_FULL)
9349             AmoebeUmwandeln(ax, ay);
9350           else if (element == EL_BD_AMOEBA)
9351             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9352         }
9353       }
9354       return;
9355     }
9356     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9357     {
9358       /* amoeba gets larger by growing in some direction */
9359
9360       int new_group_nr = AmoebaNr[ax][ay];
9361
9362 #ifdef DEBUG
9363   if (new_group_nr == 0)
9364   {
9365     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9366     printf("AmoebeAbleger(): This should never happen!\n");
9367     return;
9368   }
9369 #endif
9370
9371       AmoebaNr[newax][neway] = new_group_nr;
9372       AmoebaCnt[new_group_nr]++;
9373       AmoebaCnt2[new_group_nr]++;
9374
9375       /* if amoeba touches other amoeba(s) after growing, unify them */
9376       AmoebenVereinigen(newax, neway);
9377
9378       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9379       {
9380         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9381         return;
9382       }
9383     }
9384   }
9385
9386   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9387       (neway == lev_fieldy - 1 && newax != ax))
9388   {
9389     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9390     Store[newax][neway] = element;
9391   }
9392   else if (neway == ay || element == EL_EMC_DRIPPER)
9393   {
9394     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9395
9396     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9397   }
9398   else
9399   {
9400     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9401     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9402     Store[ax][ay] = EL_AMOEBA_DROP;
9403     ContinueMoving(ax, ay);
9404     return;
9405   }
9406
9407   TEST_DrawLevelField(newax, neway);
9408 }
9409
9410 void Life(int ax, int ay)
9411 {
9412   int x1, y1, x2, y2;
9413   int life_time = 40;
9414   int element = Feld[ax][ay];
9415   int graphic = el2img(element);
9416   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9417                          level.biomaze);
9418   boolean changed = FALSE;
9419
9420   if (IS_ANIMATED(graphic))
9421     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9422
9423   if (Stop[ax][ay])
9424     return;
9425
9426   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9427     MovDelay[ax][ay] = life_time;
9428
9429   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9430   {
9431     MovDelay[ax][ay]--;
9432     if (MovDelay[ax][ay])
9433       return;
9434   }
9435
9436   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9437   {
9438     int xx = ax+x1, yy = ay+y1;
9439     int nachbarn = 0;
9440
9441     if (!IN_LEV_FIELD(xx, yy))
9442       continue;
9443
9444     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9445     {
9446       int x = xx+x2, y = yy+y2;
9447
9448       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9449         continue;
9450
9451       if (((Feld[x][y] == element ||
9452             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9453            !Stop[x][y]) ||
9454           (IS_FREE(x, y) && Stop[x][y]))
9455         nachbarn++;
9456     }
9457
9458     if (xx == ax && yy == ay)           /* field in the middle */
9459     {
9460       if (nachbarn < life_parameter[0] ||
9461           nachbarn > life_parameter[1])
9462       {
9463         Feld[xx][yy] = EL_EMPTY;
9464         if (!Stop[xx][yy])
9465           TEST_DrawLevelField(xx, yy);
9466         Stop[xx][yy] = TRUE;
9467         changed = TRUE;
9468       }
9469     }
9470     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9471     {                                   /* free border field */
9472       if (nachbarn >= life_parameter[2] &&
9473           nachbarn <= life_parameter[3])
9474       {
9475         Feld[xx][yy] = element;
9476         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9477         if (!Stop[xx][yy])
9478           TEST_DrawLevelField(xx, yy);
9479         Stop[xx][yy] = TRUE;
9480         changed = TRUE;
9481       }
9482     }
9483   }
9484
9485   if (changed)
9486     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9487                    SND_GAME_OF_LIFE_GROWING);
9488 }
9489
9490 static void InitRobotWheel(int x, int y)
9491 {
9492   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9493 }
9494
9495 static void RunRobotWheel(int x, int y)
9496 {
9497   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9498 }
9499
9500 static void StopRobotWheel(int x, int y)
9501 {
9502   if (ZX == x && ZY == y)
9503   {
9504     ZX = ZY = -1;
9505
9506     game.robot_wheel_active = FALSE;
9507   }
9508 }
9509
9510 static void InitTimegateWheel(int x, int y)
9511 {
9512   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9513 }
9514
9515 static void RunTimegateWheel(int x, int y)
9516 {
9517   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9518 }
9519
9520 static void InitMagicBallDelay(int x, int y)
9521 {
9522 #if 1
9523   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9524 #else
9525   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9526 #endif
9527 }
9528
9529 static void ActivateMagicBall(int bx, int by)
9530 {
9531   int x, y;
9532
9533   if (level.ball_random)
9534   {
9535     int pos_border = RND(8);    /* select one of the eight border elements */
9536     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9537     int xx = pos_content % 3;
9538     int yy = pos_content / 3;
9539
9540     x = bx - 1 + xx;
9541     y = by - 1 + yy;
9542
9543     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9544       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9545   }
9546   else
9547   {
9548     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9549     {
9550       int xx = x - bx + 1;
9551       int yy = y - by + 1;
9552
9553       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9554         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9555     }
9556   }
9557
9558   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9559 }
9560
9561 void CheckExit(int x, int y)
9562 {
9563   if (local_player->gems_still_needed > 0 ||
9564       local_player->sokobanfields_still_needed > 0 ||
9565       local_player->lights_still_needed > 0)
9566   {
9567     int element = Feld[x][y];
9568     int graphic = el2img(element);
9569
9570     if (IS_ANIMATED(graphic))
9571       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9572
9573     return;
9574   }
9575
9576   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9577     return;
9578
9579   Feld[x][y] = EL_EXIT_OPENING;
9580
9581   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9582 }
9583
9584 void CheckExitEM(int x, int y)
9585 {
9586   if (local_player->gems_still_needed > 0 ||
9587       local_player->sokobanfields_still_needed > 0 ||
9588       local_player->lights_still_needed > 0)
9589   {
9590     int element = Feld[x][y];
9591     int graphic = el2img(element);
9592
9593     if (IS_ANIMATED(graphic))
9594       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9595
9596     return;
9597   }
9598
9599   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9600     return;
9601
9602   Feld[x][y] = EL_EM_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9605 }
9606
9607 void CheckExitSteel(int x, int y)
9608 {
9609   if (local_player->gems_still_needed > 0 ||
9610       local_player->sokobanfields_still_needed > 0 ||
9611       local_player->lights_still_needed > 0)
9612   {
9613     int element = Feld[x][y];
9614     int graphic = el2img(element);
9615
9616     if (IS_ANIMATED(graphic))
9617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9618
9619     return;
9620   }
9621
9622   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9623     return;
9624
9625   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9626
9627   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9628 }
9629
9630 void CheckExitSteelEM(int x, int y)
9631 {
9632   if (local_player->gems_still_needed > 0 ||
9633       local_player->sokobanfields_still_needed > 0 ||
9634       local_player->lights_still_needed > 0)
9635   {
9636     int element = Feld[x][y];
9637     int graphic = el2img(element);
9638
9639     if (IS_ANIMATED(graphic))
9640       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9641
9642     return;
9643   }
9644
9645   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9646     return;
9647
9648   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9649
9650   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9651 }
9652
9653 void CheckExitSP(int x, int y)
9654 {
9655   if (local_player->gems_still_needed > 0)
9656   {
9657     int element = Feld[x][y];
9658     int graphic = el2img(element);
9659
9660     if (IS_ANIMATED(graphic))
9661       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9662
9663     return;
9664   }
9665
9666   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9667     return;
9668
9669   Feld[x][y] = EL_SP_EXIT_OPENING;
9670
9671   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9672 }
9673
9674 static void CloseAllOpenTimegates()
9675 {
9676   int x, y;
9677
9678   SCAN_PLAYFIELD(x, y)
9679   {
9680     int element = Feld[x][y];
9681
9682     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9683     {
9684       Feld[x][y] = EL_TIMEGATE_CLOSING;
9685
9686       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9687     }
9688   }
9689 }
9690
9691 void DrawTwinkleOnField(int x, int y)
9692 {
9693   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9694     return;
9695
9696   if (Feld[x][y] == EL_BD_DIAMOND)
9697     return;
9698
9699   if (MovDelay[x][y] == 0)      /* next animation frame */
9700     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9701
9702   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9703   {
9704     MovDelay[x][y]--;
9705
9706     DrawLevelElementAnimation(x, y, Feld[x][y]);
9707
9708     if (MovDelay[x][y] != 0)
9709     {
9710       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9711                                            10 - MovDelay[x][y]);
9712
9713       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9714     }
9715   }
9716 }
9717
9718 void MauerWaechst(int x, int y)
9719 {
9720   int delay = 6;
9721
9722   if (!MovDelay[x][y])          /* next animation frame */
9723     MovDelay[x][y] = 3 * delay;
9724
9725   if (MovDelay[x][y])           /* wait some time before next frame */
9726   {
9727     MovDelay[x][y]--;
9728
9729     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9730     {
9731       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9732       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9733
9734       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9735     }
9736
9737     if (!MovDelay[x][y])
9738     {
9739       if (MovDir[x][y] == MV_LEFT)
9740       {
9741         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9742           TEST_DrawLevelField(x - 1, y);
9743       }
9744       else if (MovDir[x][y] == MV_RIGHT)
9745       {
9746         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9747           TEST_DrawLevelField(x + 1, y);
9748       }
9749       else if (MovDir[x][y] == MV_UP)
9750       {
9751         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9752           TEST_DrawLevelField(x, y - 1);
9753       }
9754       else
9755       {
9756         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9757           TEST_DrawLevelField(x, y + 1);
9758       }
9759
9760       Feld[x][y] = Store[x][y];
9761       Store[x][y] = 0;
9762       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9763       TEST_DrawLevelField(x, y);
9764     }
9765   }
9766 }
9767
9768 void MauerAbleger(int ax, int ay)
9769 {
9770   int element = Feld[ax][ay];
9771   int graphic = el2img(element);
9772   boolean oben_frei = FALSE, unten_frei = FALSE;
9773   boolean links_frei = FALSE, rechts_frei = FALSE;
9774   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9775   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9776   boolean new_wall = FALSE;
9777
9778   if (IS_ANIMATED(graphic))
9779     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9780
9781   if (!MovDelay[ax][ay])        /* start building new wall */
9782     MovDelay[ax][ay] = 6;
9783
9784   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9785   {
9786     MovDelay[ax][ay]--;
9787     if (MovDelay[ax][ay])
9788       return;
9789   }
9790
9791   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9792     oben_frei = TRUE;
9793   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9794     unten_frei = TRUE;
9795   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9796     links_frei = TRUE;
9797   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9798     rechts_frei = TRUE;
9799
9800   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9801       element == EL_EXPANDABLE_WALL_ANY)
9802   {
9803     if (oben_frei)
9804     {
9805       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9806       Store[ax][ay-1] = element;
9807       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9808       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9809         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9810                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9811       new_wall = TRUE;
9812     }
9813     if (unten_frei)
9814     {
9815       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9816       Store[ax][ay+1] = element;
9817       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9818       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9819         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9820                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9821       new_wall = TRUE;
9822     }
9823   }
9824
9825   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9826       element == EL_EXPANDABLE_WALL_ANY ||
9827       element == EL_EXPANDABLE_WALL ||
9828       element == EL_BD_EXPANDABLE_WALL)
9829   {
9830     if (links_frei)
9831     {
9832       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9833       Store[ax-1][ay] = element;
9834       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9835       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9836         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9837                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9838       new_wall = TRUE;
9839     }
9840
9841     if (rechts_frei)
9842     {
9843       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9844       Store[ax+1][ay] = element;
9845       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9846       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9847         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9848                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9849       new_wall = TRUE;
9850     }
9851   }
9852
9853   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9854     TEST_DrawLevelField(ax, ay);
9855
9856   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9857     oben_massiv = TRUE;
9858   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9859     unten_massiv = TRUE;
9860   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9861     links_massiv = TRUE;
9862   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9863     rechts_massiv = TRUE;
9864
9865   if (((oben_massiv && unten_massiv) ||
9866        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9867        element == EL_EXPANDABLE_WALL) &&
9868       ((links_massiv && rechts_massiv) ||
9869        element == EL_EXPANDABLE_WALL_VERTICAL))
9870     Feld[ax][ay] = EL_WALL;
9871
9872   if (new_wall)
9873     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9874 }
9875
9876 void MauerAblegerStahl(int ax, int ay)
9877 {
9878   int element = Feld[ax][ay];
9879   int graphic = el2img(element);
9880   boolean oben_frei = FALSE, unten_frei = FALSE;
9881   boolean links_frei = FALSE, rechts_frei = FALSE;
9882   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9883   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9884   boolean new_wall = FALSE;
9885
9886   if (IS_ANIMATED(graphic))
9887     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9888
9889   if (!MovDelay[ax][ay])        /* start building new wall */
9890     MovDelay[ax][ay] = 6;
9891
9892   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9893   {
9894     MovDelay[ax][ay]--;
9895     if (MovDelay[ax][ay])
9896       return;
9897   }
9898
9899   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9900     oben_frei = TRUE;
9901   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9902     unten_frei = TRUE;
9903   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9904     links_frei = TRUE;
9905   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9906     rechts_frei = TRUE;
9907
9908   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9909       element == EL_EXPANDABLE_STEELWALL_ANY)
9910   {
9911     if (oben_frei)
9912     {
9913       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9914       Store[ax][ay-1] = element;
9915       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9916       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9917         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9918                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9919       new_wall = TRUE;
9920     }
9921     if (unten_frei)
9922     {
9923       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9924       Store[ax][ay+1] = element;
9925       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9926       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9927         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9928                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9929       new_wall = TRUE;
9930     }
9931   }
9932
9933   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9934       element == EL_EXPANDABLE_STEELWALL_ANY)
9935   {
9936     if (links_frei)
9937     {
9938       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9939       Store[ax-1][ay] = element;
9940       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9941       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9942         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9943                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9944       new_wall = TRUE;
9945     }
9946
9947     if (rechts_frei)
9948     {
9949       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9950       Store[ax+1][ay] = element;
9951       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9952       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9953         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9954                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9955       new_wall = TRUE;
9956     }
9957   }
9958
9959   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9960     oben_massiv = TRUE;
9961   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9962     unten_massiv = TRUE;
9963   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9964     links_massiv = TRUE;
9965   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9966     rechts_massiv = TRUE;
9967
9968   if (((oben_massiv && unten_massiv) ||
9969        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9970       ((links_massiv && rechts_massiv) ||
9971        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9972     Feld[ax][ay] = EL_STEELWALL;
9973
9974   if (new_wall)
9975     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9976 }
9977
9978 void CheckForDragon(int x, int y)
9979 {
9980   int i, j;
9981   boolean dragon_found = FALSE;
9982   static int xy[4][2] =
9983   {
9984     { 0, -1 },
9985     { -1, 0 },
9986     { +1, 0 },
9987     { 0, +1 }
9988   };
9989
9990   for (i = 0; i < NUM_DIRECTIONS; i++)
9991   {
9992     for (j = 0; j < 4; j++)
9993     {
9994       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9995
9996       if (IN_LEV_FIELD(xx, yy) &&
9997           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9998       {
9999         if (Feld[xx][yy] == EL_DRAGON)
10000           dragon_found = TRUE;
10001       }
10002       else
10003         break;
10004     }
10005   }
10006
10007   if (!dragon_found)
10008   {
10009     for (i = 0; i < NUM_DIRECTIONS; i++)
10010     {
10011       for (j = 0; j < 3; j++)
10012       {
10013         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10014   
10015         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10016         {
10017           Feld[xx][yy] = EL_EMPTY;
10018           TEST_DrawLevelField(xx, yy);
10019         }
10020         else
10021           break;
10022       }
10023     }
10024   }
10025 }
10026
10027 static void InitBuggyBase(int x, int y)
10028 {
10029   int element = Feld[x][y];
10030   int activating_delay = FRAMES_PER_SECOND / 4;
10031
10032   ChangeDelay[x][y] =
10033     (element == EL_SP_BUGGY_BASE ?
10034      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10035      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10036      activating_delay :
10037      element == EL_SP_BUGGY_BASE_ACTIVE ?
10038      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10039 }
10040
10041 static void WarnBuggyBase(int x, int y)
10042 {
10043   int i;
10044   static int xy[4][2] =
10045   {
10046     { 0, -1 },
10047     { -1, 0 },
10048     { +1, 0 },
10049     { 0, +1 }
10050   };
10051
10052   for (i = 0; i < NUM_DIRECTIONS; i++)
10053   {
10054     int xx = x + xy[i][0];
10055     int yy = y + xy[i][1];
10056
10057     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10058     {
10059       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10060
10061       break;
10062     }
10063   }
10064 }
10065
10066 static void InitTrap(int x, int y)
10067 {
10068   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10069 }
10070
10071 static void ActivateTrap(int x, int y)
10072 {
10073   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10074 }
10075
10076 static void ChangeActiveTrap(int x, int y)
10077 {
10078   int graphic = IMG_TRAP_ACTIVE;
10079
10080   /* if new animation frame was drawn, correct crumbled sand border */
10081   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10082     TEST_DrawLevelFieldCrumbledSand(x, y);
10083 }
10084
10085 static int getSpecialActionElement(int element, int number, int base_element)
10086 {
10087   return (element != EL_EMPTY ? element :
10088           number != -1 ? base_element + number - 1 :
10089           EL_EMPTY);
10090 }
10091
10092 static int getModifiedActionNumber(int value_old, int operator, int operand,
10093                                    int value_min, int value_max)
10094 {
10095   int value_new = (operator == CA_MODE_SET      ? operand :
10096                    operator == CA_MODE_ADD      ? value_old + operand :
10097                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10098                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10099                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10100                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10101                    value_old);
10102
10103   return (value_new < value_min ? value_min :
10104           value_new > value_max ? value_max :
10105           value_new);
10106 }
10107
10108 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10109 {
10110   struct ElementInfo *ei = &element_info[element];
10111   struct ElementChangeInfo *change = &ei->change_page[page];
10112   int target_element = change->target_element;
10113   int action_type = change->action_type;
10114   int action_mode = change->action_mode;
10115   int action_arg = change->action_arg;
10116   int i;
10117
10118   if (!change->has_action)
10119     return;
10120
10121   /* ---------- determine action paramater values -------------------------- */
10122
10123   int level_time_value =
10124     (level.time > 0 ? TimeLeft :
10125      TimePlayed);
10126
10127   int action_arg_element =
10128     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10129      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10130      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10131      EL_EMPTY);
10132
10133   int action_arg_direction =
10134     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10135      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10136      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10137      change->actual_trigger_side :
10138      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10139      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10140      MV_NONE);
10141
10142   int action_arg_number_min =
10143     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10144      CA_ARG_MIN);
10145
10146   int action_arg_number_max =
10147     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10148      action_type == CA_SET_LEVEL_GEMS ? 999 :
10149      action_type == CA_SET_LEVEL_TIME ? 9999 :
10150      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10151      action_type == CA_SET_CE_VALUE ? 9999 :
10152      action_type == CA_SET_CE_SCORE ? 9999 :
10153      CA_ARG_MAX);
10154
10155   int action_arg_number_reset =
10156     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10157      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10158      action_type == CA_SET_LEVEL_TIME ? level.time :
10159      action_type == CA_SET_LEVEL_SCORE ? 0 :
10160 #if USE_NEW_CUSTOM_VALUE
10161      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10162 #else
10163      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10164 #endif
10165      action_type == CA_SET_CE_SCORE ? 0 :
10166      0);
10167
10168   int action_arg_number =
10169     (action_arg <= CA_ARG_MAX ? action_arg :
10170      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10171      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10172      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10173      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10174      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10175      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10176 #if USE_NEW_CUSTOM_VALUE
10177      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10178 #else
10179      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10180 #endif
10181      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10182      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10183      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10184      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10185      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10186      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10187      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10188      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10189      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10190      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10191      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10192      -1);
10193
10194   int action_arg_number_old =
10195     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10196      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10197      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10198      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10199      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10200      0);
10201
10202   int action_arg_number_new =
10203     getModifiedActionNumber(action_arg_number_old,
10204                             action_mode, action_arg_number,
10205                             action_arg_number_min, action_arg_number_max);
10206
10207 #if 1
10208   int trigger_player_bits = change->actual_trigger_player_bits;
10209 #else
10210   int trigger_player_bits =
10211     (change->actual_trigger_player >= EL_PLAYER_1 &&
10212      change->actual_trigger_player <= EL_PLAYER_4 ?
10213      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10214      PLAYER_BITS_ANY);
10215 #endif
10216
10217   int action_arg_player_bits =
10218     (action_arg >= CA_ARG_PLAYER_1 &&
10219      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10220      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10221      PLAYER_BITS_ANY);
10222
10223   /* ---------- execute action  -------------------------------------------- */
10224
10225   switch (action_type)
10226   {
10227     case CA_NO_ACTION:
10228     {
10229       return;
10230     }
10231
10232     /* ---------- level actions  ------------------------------------------- */
10233
10234     case CA_RESTART_LEVEL:
10235     {
10236       game.restart_level = TRUE;
10237
10238       break;
10239     }
10240
10241     case CA_SHOW_ENVELOPE:
10242     {
10243       int element = getSpecialActionElement(action_arg_element,
10244                                             action_arg_number, EL_ENVELOPE_1);
10245
10246       if (IS_ENVELOPE(element))
10247         local_player->show_envelope = element;
10248
10249       break;
10250     }
10251
10252     case CA_SET_LEVEL_TIME:
10253     {
10254       if (level.time > 0)       /* only modify limited time value */
10255       {
10256         TimeLeft = action_arg_number_new;
10257
10258 #if 1
10259         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10260
10261         DisplayGameControlValues();
10262 #else
10263         DrawGameValue_Time(TimeLeft);
10264 #endif
10265
10266         if (!TimeLeft && setup.time_limit)
10267           for (i = 0; i < MAX_PLAYERS; i++)
10268             KillPlayer(&stored_player[i]);
10269       }
10270
10271       break;
10272     }
10273
10274     case CA_SET_LEVEL_SCORE:
10275     {
10276       local_player->score = action_arg_number_new;
10277
10278 #if 1
10279       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10280
10281       DisplayGameControlValues();
10282 #else
10283       DrawGameValue_Score(local_player->score);
10284 #endif
10285
10286       break;
10287     }
10288
10289     case CA_SET_LEVEL_GEMS:
10290     {
10291       local_player->gems_still_needed = action_arg_number_new;
10292
10293 #if 1
10294       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10295
10296       DisplayGameControlValues();
10297 #else
10298       DrawGameValue_Emeralds(local_player->gems_still_needed);
10299 #endif
10300
10301       break;
10302     }
10303
10304 #if !USE_PLAYER_GRAVITY
10305     case CA_SET_LEVEL_GRAVITY:
10306     {
10307       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10308                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10309                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10310                       game.gravity);
10311       break;
10312     }
10313 #endif
10314
10315     case CA_SET_LEVEL_WIND:
10316     {
10317       game.wind_direction = action_arg_direction;
10318
10319       break;
10320     }
10321
10322     /* ---------- player actions  ------------------------------------------ */
10323
10324     case CA_MOVE_PLAYER:
10325     {
10326       /* automatically move to the next field in specified direction */
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328         if (trigger_player_bits & (1 << i))
10329           stored_player[i].programmed_action = action_arg_direction;
10330
10331       break;
10332     }
10333
10334     case CA_EXIT_PLAYER:
10335     {
10336       for (i = 0; i < MAX_PLAYERS; i++)
10337         if (action_arg_player_bits & (1 << i))
10338           PlayerWins(&stored_player[i]);
10339
10340       break;
10341     }
10342
10343     case CA_KILL_PLAYER:
10344     {
10345       for (i = 0; i < MAX_PLAYERS; i++)
10346         if (action_arg_player_bits & (1 << i))
10347           KillPlayer(&stored_player[i]);
10348
10349       break;
10350     }
10351
10352     case CA_SET_PLAYER_KEYS:
10353     {
10354       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10355       int element = getSpecialActionElement(action_arg_element,
10356                                             action_arg_number, EL_KEY_1);
10357
10358       if (IS_KEY(element))
10359       {
10360         for (i = 0; i < MAX_PLAYERS; i++)
10361         {
10362           if (trigger_player_bits & (1 << i))
10363           {
10364             stored_player[i].key[KEY_NR(element)] = key_state;
10365
10366             DrawGameDoorValues();
10367           }
10368         }
10369       }
10370
10371       break;
10372     }
10373
10374     case CA_SET_PLAYER_SPEED:
10375     {
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377       {
10378         if (trigger_player_bits & (1 << i))
10379         {
10380           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10381
10382           if (action_arg == CA_ARG_SPEED_FASTER &&
10383               stored_player[i].cannot_move)
10384           {
10385             action_arg_number = STEPSIZE_VERY_SLOW;
10386           }
10387           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10388                    action_arg == CA_ARG_SPEED_FASTER)
10389           {
10390             action_arg_number = 2;
10391             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10392                            CA_MODE_MULTIPLY);
10393           }
10394           else if (action_arg == CA_ARG_NUMBER_RESET)
10395           {
10396             action_arg_number = level.initial_player_stepsize[i];
10397           }
10398
10399           move_stepsize =
10400             getModifiedActionNumber(move_stepsize,
10401                                     action_mode,
10402                                     action_arg_number,
10403                                     action_arg_number_min,
10404                                     action_arg_number_max);
10405
10406           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10407         }
10408       }
10409
10410       break;
10411     }
10412
10413     case CA_SET_PLAYER_SHIELD:
10414     {
10415       for (i = 0; i < MAX_PLAYERS; i++)
10416       {
10417         if (trigger_player_bits & (1 << i))
10418         {
10419           if (action_arg == CA_ARG_SHIELD_OFF)
10420           {
10421             stored_player[i].shield_normal_time_left = 0;
10422             stored_player[i].shield_deadly_time_left = 0;
10423           }
10424           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10425           {
10426             stored_player[i].shield_normal_time_left = 999999;
10427           }
10428           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10429           {
10430             stored_player[i].shield_normal_time_left = 999999;
10431             stored_player[i].shield_deadly_time_left = 999999;
10432           }
10433         }
10434       }
10435
10436       break;
10437     }
10438
10439 #if USE_PLAYER_GRAVITY
10440     case CA_SET_PLAYER_GRAVITY:
10441     {
10442       for (i = 0; i < MAX_PLAYERS; i++)
10443       {
10444         if (trigger_player_bits & (1 << i))
10445         {
10446           stored_player[i].gravity =
10447             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10448              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10449              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10450              stored_player[i].gravity);
10451         }
10452       }
10453
10454       break;
10455     }
10456 #endif
10457
10458     case CA_SET_PLAYER_ARTWORK:
10459     {
10460       for (i = 0; i < MAX_PLAYERS; i++)
10461       {
10462         if (trigger_player_bits & (1 << i))
10463         {
10464           int artwork_element = action_arg_element;
10465
10466           if (action_arg == CA_ARG_ELEMENT_RESET)
10467             artwork_element =
10468               (level.use_artwork_element[i] ? level.artwork_element[i] :
10469                stored_player[i].element_nr);
10470
10471 #if USE_GFX_RESET_PLAYER_ARTWORK
10472           if (stored_player[i].artwork_element != artwork_element)
10473             stored_player[i].Frame = 0;
10474 #endif
10475
10476           stored_player[i].artwork_element = artwork_element;
10477
10478           SetPlayerWaiting(&stored_player[i], FALSE);
10479
10480           /* set number of special actions for bored and sleeping animation */
10481           stored_player[i].num_special_action_bored =
10482             get_num_special_action(artwork_element,
10483                                    ACTION_BORING_1, ACTION_BORING_LAST);
10484           stored_player[i].num_special_action_sleeping =
10485             get_num_special_action(artwork_element,
10486                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10487         }
10488       }
10489
10490       break;
10491     }
10492
10493     /* ---------- CE actions  ---------------------------------------------- */
10494
10495     case CA_SET_CE_VALUE:
10496     {
10497 #if USE_NEW_CUSTOM_VALUE
10498       int last_ce_value = CustomValue[x][y];
10499
10500       CustomValue[x][y] = action_arg_number_new;
10501
10502       if (CustomValue[x][y] != last_ce_value)
10503       {
10504         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10505         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10506
10507         if (CustomValue[x][y] == 0)
10508         {
10509           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10510           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10511         }
10512       }
10513 #endif
10514
10515       break;
10516     }
10517
10518     case CA_SET_CE_SCORE:
10519     {
10520 #if USE_NEW_CUSTOM_VALUE
10521       int last_ce_score = ei->collect_score;
10522
10523       ei->collect_score = action_arg_number_new;
10524
10525       if (ei->collect_score != last_ce_score)
10526       {
10527         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10528         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10529
10530         if (ei->collect_score == 0)
10531         {
10532           int xx, yy;
10533
10534           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10535           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10536
10537           /*
10538             This is a very special case that seems to be a mixture between
10539             CheckElementChange() and CheckTriggeredElementChange(): while
10540             the first one only affects single elements that are triggered
10541             directly, the second one affects multiple elements in the playfield
10542             that are triggered indirectly by another element. This is a third
10543             case: Changing the CE score always affects multiple identical CEs,
10544             so every affected CE must be checked, not only the single CE for
10545             which the CE score was changed in the first place (as every instance
10546             of that CE shares the same CE score, and therefore also can change)!
10547           */
10548           SCAN_PLAYFIELD(xx, yy)
10549           {
10550             if (Feld[xx][yy] == element)
10551               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10552                                  CE_SCORE_GETS_ZERO);
10553           }
10554         }
10555       }
10556 #endif
10557
10558       break;
10559     }
10560
10561     /* ---------- engine actions  ------------------------------------------ */
10562
10563     case CA_SET_ENGINE_SCAN_MODE:
10564     {
10565       InitPlayfieldScanMode(action_arg);
10566
10567       break;
10568     }
10569
10570     default:
10571       break;
10572   }
10573 }
10574
10575 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10576 {
10577   int old_element = Feld[x][y];
10578   int new_element = GetElementFromGroupElement(element);
10579   int previous_move_direction = MovDir[x][y];
10580 #if USE_NEW_CUSTOM_VALUE
10581   int last_ce_value = CustomValue[x][y];
10582 #endif
10583   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10584   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10585   boolean add_player_onto_element = (new_element_is_player &&
10586 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10587                                      /* this breaks SnakeBite when a snake is
10588                                         halfway through a door that closes */
10589                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10590                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10591 #endif
10592                                      IS_WALKABLE(old_element));
10593
10594 #if 0
10595   /* check if element under the player changes from accessible to unaccessible
10596      (needed for special case of dropping element which then changes) */
10597   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10598       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10599   {
10600     Bang(x, y);
10601
10602     return;
10603   }
10604 #endif
10605
10606   if (!add_player_onto_element)
10607   {
10608     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10609       RemoveMovingField(x, y);
10610     else
10611       RemoveField(x, y);
10612
10613     Feld[x][y] = new_element;
10614
10615 #if !USE_GFX_RESET_GFX_ANIMATION
10616     ResetGfxAnimation(x, y);
10617     ResetRandomAnimationValue(x, y);
10618 #endif
10619
10620     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10621       MovDir[x][y] = previous_move_direction;
10622
10623 #if USE_NEW_CUSTOM_VALUE
10624     if (element_info[new_element].use_last_ce_value)
10625       CustomValue[x][y] = last_ce_value;
10626 #endif
10627
10628     InitField_WithBug1(x, y, FALSE);
10629
10630     new_element = Feld[x][y];   /* element may have changed */
10631
10632 #if USE_GFX_RESET_GFX_ANIMATION
10633     ResetGfxAnimation(x, y);
10634     ResetRandomAnimationValue(x, y);
10635 #endif
10636
10637     TEST_DrawLevelField(x, y);
10638
10639     if (GFX_CRUMBLED(new_element))
10640       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10641   }
10642
10643 #if 1
10644   /* check if element under the player changes from accessible to unaccessible
10645      (needed for special case of dropping element which then changes) */
10646   /* (must be checked after creating new element for walkable group elements) */
10647 #if USE_FIX_KILLED_BY_NON_WALKABLE
10648   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10649       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10650   {
10651     Bang(x, y);
10652
10653     return;
10654   }
10655 #else
10656   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10657       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10658   {
10659     Bang(x, y);
10660
10661     return;
10662   }
10663 #endif
10664 #endif
10665
10666   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10667   if (new_element_is_player)
10668     RelocatePlayer(x, y, new_element);
10669
10670   if (is_change)
10671     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10672
10673   TestIfBadThingTouchesPlayer(x, y);
10674   TestIfPlayerTouchesCustomElement(x, y);
10675   TestIfElementTouchesCustomElement(x, y);
10676 }
10677
10678 static void CreateField(int x, int y, int element)
10679 {
10680   CreateFieldExt(x, y, element, FALSE);
10681 }
10682
10683 static void CreateElementFromChange(int x, int y, int element)
10684 {
10685   element = GET_VALID_RUNTIME_ELEMENT(element);
10686
10687 #if USE_STOP_CHANGED_ELEMENTS
10688   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10689   {
10690     int old_element = Feld[x][y];
10691
10692     /* prevent changed element from moving in same engine frame
10693        unless both old and new element can either fall or move */
10694     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10695         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10696       Stop[x][y] = TRUE;
10697   }
10698 #endif
10699
10700   CreateFieldExt(x, y, element, TRUE);
10701 }
10702
10703 static boolean ChangeElement(int x, int y, int element, int page)
10704 {
10705   struct ElementInfo *ei = &element_info[element];
10706   struct ElementChangeInfo *change = &ei->change_page[page];
10707   int ce_value = CustomValue[x][y];
10708   int ce_score = ei->collect_score;
10709   int target_element;
10710   int old_element = Feld[x][y];
10711
10712   /* always use default change event to prevent running into a loop */
10713   if (ChangeEvent[x][y] == -1)
10714     ChangeEvent[x][y] = CE_DELAY;
10715
10716   if (ChangeEvent[x][y] == CE_DELAY)
10717   {
10718     /* reset actual trigger element, trigger player and action element */
10719     change->actual_trigger_element = EL_EMPTY;
10720     change->actual_trigger_player = EL_PLAYER_1;
10721     change->actual_trigger_player_bits = CH_PLAYER_1;
10722     change->actual_trigger_side = CH_SIDE_NONE;
10723     change->actual_trigger_ce_value = 0;
10724     change->actual_trigger_ce_score = 0;
10725   }
10726
10727   /* do not change elements more than a specified maximum number of changes */
10728   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10729     return FALSE;
10730
10731   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10732
10733   if (change->explode)
10734   {
10735     Bang(x, y);
10736
10737     return TRUE;
10738   }
10739
10740   if (change->use_target_content)
10741   {
10742     boolean complete_replace = TRUE;
10743     boolean can_replace[3][3];
10744     int xx, yy;
10745
10746     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10747     {
10748       boolean is_empty;
10749       boolean is_walkable;
10750       boolean is_diggable;
10751       boolean is_collectible;
10752       boolean is_removable;
10753       boolean is_destructible;
10754       int ex = x + xx - 1;
10755       int ey = y + yy - 1;
10756       int content_element = change->target_content.e[xx][yy];
10757       int e;
10758
10759       can_replace[xx][yy] = TRUE;
10760
10761       if (ex == x && ey == y)   /* do not check changing element itself */
10762         continue;
10763
10764       if (content_element == EL_EMPTY_SPACE)
10765       {
10766         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10767
10768         continue;
10769       }
10770
10771       if (!IN_LEV_FIELD(ex, ey))
10772       {
10773         can_replace[xx][yy] = FALSE;
10774         complete_replace = FALSE;
10775
10776         continue;
10777       }
10778
10779       e = Feld[ex][ey];
10780
10781       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10782         e = MovingOrBlocked2Element(ex, ey);
10783
10784       is_empty = (IS_FREE(ex, ey) ||
10785                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10786
10787       is_walkable     = (is_empty || IS_WALKABLE(e));
10788       is_diggable     = (is_empty || IS_DIGGABLE(e));
10789       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10790       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10791       is_removable    = (is_diggable || is_collectible);
10792
10793       can_replace[xx][yy] =
10794         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10795           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10796           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10797           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10798           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10799           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10800          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10801
10802       if (!can_replace[xx][yy])
10803         complete_replace = FALSE;
10804     }
10805
10806     if (!change->only_if_complete || complete_replace)
10807     {
10808       boolean something_has_changed = FALSE;
10809
10810       if (change->only_if_complete && change->use_random_replace &&
10811           RND(100) < change->random_percentage)
10812         return FALSE;
10813
10814       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10815       {
10816         int ex = x + xx - 1;
10817         int ey = y + yy - 1;
10818         int content_element;
10819
10820         if (can_replace[xx][yy] && (!change->use_random_replace ||
10821                                     RND(100) < change->random_percentage))
10822         {
10823           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10824             RemoveMovingField(ex, ey);
10825
10826           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10827
10828           content_element = change->target_content.e[xx][yy];
10829           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10830                                               ce_value, ce_score);
10831
10832           CreateElementFromChange(ex, ey, target_element);
10833
10834           something_has_changed = TRUE;
10835
10836           /* for symmetry reasons, freeze newly created border elements */
10837           if (ex != x || ey != y)
10838             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10839         }
10840       }
10841
10842       if (something_has_changed)
10843       {
10844         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10845         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10846       }
10847     }
10848   }
10849   else
10850   {
10851     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10852                                         ce_value, ce_score);
10853
10854     if (element == EL_DIAGONAL_GROWING ||
10855         element == EL_DIAGONAL_SHRINKING)
10856     {
10857       target_element = Store[x][y];
10858
10859       Store[x][y] = EL_EMPTY;
10860     }
10861
10862     CreateElementFromChange(x, y, target_element);
10863
10864     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10865     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10866   }
10867
10868   /* this uses direct change before indirect change */
10869   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10870
10871   return TRUE;
10872 }
10873
10874 #if USE_NEW_DELAYED_ACTION
10875
10876 static void HandleElementChange(int x, int y, int page)
10877 {
10878   int element = MovingOrBlocked2Element(x, y);
10879   struct ElementInfo *ei = &element_info[element];
10880   struct ElementChangeInfo *change = &ei->change_page[page];
10881
10882 #ifdef DEBUG
10883   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10884       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10885   {
10886     printf("\n\n");
10887     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10888            x, y, element, element_info[element].token_name);
10889     printf("HandleElementChange(): This should never happen!\n");
10890     printf("\n\n");
10891   }
10892 #endif
10893
10894   /* this can happen with classic bombs on walkable, changing elements */
10895   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10896   {
10897 #if 0
10898     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10899       ChangeDelay[x][y] = 0;
10900 #endif
10901
10902     return;
10903   }
10904
10905   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10906   {
10907     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10908
10909     if (change->can_change)
10910     {
10911 #if 1
10912       /* !!! not clear why graphic animation should be reset at all here !!! */
10913       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10914 #if USE_GFX_RESET_WHEN_NOT_MOVING
10915       /* when a custom element is about to change (for example by change delay),
10916          do not reset graphic animation when the custom element is moving */
10917       if (!IS_MOVING(x, y))
10918 #endif
10919       {
10920         ResetGfxAnimation(x, y);
10921         ResetRandomAnimationValue(x, y);
10922       }
10923 #endif
10924
10925       if (change->pre_change_function)
10926         change->pre_change_function(x, y);
10927     }
10928   }
10929
10930   ChangeDelay[x][y]--;
10931
10932   if (ChangeDelay[x][y] != 0)           /* continue element change */
10933   {
10934     if (change->can_change)
10935     {
10936       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10937
10938       if (IS_ANIMATED(graphic))
10939         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10940
10941       if (change->change_function)
10942         change->change_function(x, y);
10943     }
10944   }
10945   else                                  /* finish element change */
10946   {
10947     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10948     {
10949       page = ChangePage[x][y];
10950       ChangePage[x][y] = -1;
10951
10952       change = &ei->change_page[page];
10953     }
10954
10955     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10956     {
10957       ChangeDelay[x][y] = 1;            /* try change after next move step */
10958       ChangePage[x][y] = page;          /* remember page to use for change */
10959
10960       return;
10961     }
10962
10963     if (change->can_change)
10964     {
10965       if (ChangeElement(x, y, element, page))
10966       {
10967         if (change->post_change_function)
10968           change->post_change_function(x, y);
10969       }
10970     }
10971
10972     if (change->has_action)
10973       ExecuteCustomElementAction(x, y, element, page);
10974   }
10975 }
10976
10977 #else
10978
10979 static void HandleElementChange(int x, int y, int page)
10980 {
10981   int element = MovingOrBlocked2Element(x, y);
10982   struct ElementInfo *ei = &element_info[element];
10983   struct ElementChangeInfo *change = &ei->change_page[page];
10984
10985 #ifdef DEBUG
10986   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10987   {
10988     printf("\n\n");
10989     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10990            x, y, element, element_info[element].token_name);
10991     printf("HandleElementChange(): This should never happen!\n");
10992     printf("\n\n");
10993   }
10994 #endif
10995
10996   /* this can happen with classic bombs on walkable, changing elements */
10997   if (!CAN_CHANGE(element))
10998   {
10999 #if 0
11000     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11001       ChangeDelay[x][y] = 0;
11002 #endif
11003
11004     return;
11005   }
11006
11007   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11008   {
11009     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11010
11011     ResetGfxAnimation(x, y);
11012     ResetRandomAnimationValue(x, y);
11013
11014     if (change->pre_change_function)
11015       change->pre_change_function(x, y);
11016   }
11017
11018   ChangeDelay[x][y]--;
11019
11020   if (ChangeDelay[x][y] != 0)           /* continue element change */
11021   {
11022     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11023
11024     if (IS_ANIMATED(graphic))
11025       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11026
11027     if (change->change_function)
11028       change->change_function(x, y);
11029   }
11030   else                                  /* finish element change */
11031   {
11032     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11033     {
11034       page = ChangePage[x][y];
11035       ChangePage[x][y] = -1;
11036
11037       change = &ei->change_page[page];
11038     }
11039
11040     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11041     {
11042       ChangeDelay[x][y] = 1;            /* try change after next move step */
11043       ChangePage[x][y] = page;          /* remember page to use for change */
11044
11045       return;
11046     }
11047
11048     if (ChangeElement(x, y, element, page))
11049     {
11050       if (change->post_change_function)
11051         change->post_change_function(x, y);
11052     }
11053   }
11054 }
11055
11056 #endif
11057
11058 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11059                                               int trigger_element,
11060                                               int trigger_event,
11061                                               int trigger_player,
11062                                               int trigger_side,
11063                                               int trigger_page)
11064 {
11065   boolean change_done_any = FALSE;
11066   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11067   int i;
11068
11069   if (!(trigger_events[trigger_element][trigger_event]))
11070     return FALSE;
11071
11072 #if 0
11073   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11074          trigger_event, recursion_loop_depth, recursion_loop_detected,
11075          recursion_loop_element, EL_NAME(recursion_loop_element));
11076 #endif
11077
11078   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11079
11080   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11081   {
11082     int element = EL_CUSTOM_START + i;
11083     boolean change_done = FALSE;
11084     int p;
11085
11086     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11087         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11088       continue;
11089
11090     for (p = 0; p < element_info[element].num_change_pages; p++)
11091     {
11092       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11093
11094       if (change->can_change_or_has_action &&
11095           change->has_event[trigger_event] &&
11096           change->trigger_side & trigger_side &&
11097           change->trigger_player & trigger_player &&
11098           change->trigger_page & trigger_page_bits &&
11099           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11100       {
11101         change->actual_trigger_element = trigger_element;
11102         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11103         change->actual_trigger_player_bits = trigger_player;
11104         change->actual_trigger_side = trigger_side;
11105         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11106         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11107
11108         if ((change->can_change && !change_done) || change->has_action)
11109         {
11110           int x, y;
11111
11112           SCAN_PLAYFIELD(x, y)
11113           {
11114             if (Feld[x][y] == element)
11115             {
11116               if (change->can_change && !change_done)
11117               {
11118                 ChangeDelay[x][y] = 1;
11119                 ChangeEvent[x][y] = trigger_event;
11120
11121                 HandleElementChange(x, y, p);
11122               }
11123 #if USE_NEW_DELAYED_ACTION
11124               else if (change->has_action)
11125               {
11126                 ExecuteCustomElementAction(x, y, element, p);
11127                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11128               }
11129 #else
11130               if (change->has_action)
11131               {
11132                 ExecuteCustomElementAction(x, y, element, p);
11133                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11134               }
11135 #endif
11136             }
11137           }
11138
11139           if (change->can_change)
11140           {
11141             change_done = TRUE;
11142             change_done_any = TRUE;
11143           }
11144         }
11145       }
11146     }
11147   }
11148
11149   RECURSION_LOOP_DETECTION_END();
11150
11151   return change_done_any;
11152 }
11153
11154 static boolean CheckElementChangeExt(int x, int y,
11155                                      int element,
11156                                      int trigger_element,
11157                                      int trigger_event,
11158                                      int trigger_player,
11159                                      int trigger_side)
11160 {
11161   boolean change_done = FALSE;
11162   int p;
11163
11164   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11165       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11166     return FALSE;
11167
11168   if (Feld[x][y] == EL_BLOCKED)
11169   {
11170     Blocked2Moving(x, y, &x, &y);
11171     element = Feld[x][y];
11172   }
11173
11174 #if 0
11175   /* check if element has already changed */
11176   if (Feld[x][y] != element)
11177     return FALSE;
11178 #else
11179   /* check if element has already changed or is about to change after moving */
11180   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11181        Feld[x][y] != element) ||
11182
11183       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11184        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11185         ChangePage[x][y] != -1)))
11186     return FALSE;
11187 #endif
11188
11189 #if 0
11190   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11191          trigger_event, recursion_loop_depth, recursion_loop_detected,
11192          recursion_loop_element, EL_NAME(recursion_loop_element));
11193 #endif
11194
11195   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11196
11197   for (p = 0; p < element_info[element].num_change_pages; p++)
11198   {
11199     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11200
11201     /* check trigger element for all events where the element that is checked
11202        for changing interacts with a directly adjacent element -- this is
11203        different to element changes that affect other elements to change on the
11204        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11205     boolean check_trigger_element =
11206       (trigger_event == CE_TOUCHING_X ||
11207        trigger_event == CE_HITTING_X ||
11208        trigger_event == CE_HIT_BY_X ||
11209 #if 1
11210        /* this one was forgotten until 3.2.3 */
11211        trigger_event == CE_DIGGING_X);
11212 #endif
11213
11214     if (change->can_change_or_has_action &&
11215         change->has_event[trigger_event] &&
11216         change->trigger_side & trigger_side &&
11217         change->trigger_player & trigger_player &&
11218         (!check_trigger_element ||
11219          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11220     {
11221       change->actual_trigger_element = trigger_element;
11222       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11223       change->actual_trigger_player_bits = trigger_player;
11224       change->actual_trigger_side = trigger_side;
11225       change->actual_trigger_ce_value = CustomValue[x][y];
11226       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11227
11228       /* special case: trigger element not at (x,y) position for some events */
11229       if (check_trigger_element)
11230       {
11231         static struct
11232         {
11233           int dx, dy;
11234         } move_xy[] =
11235           {
11236             {  0,  0 },
11237             { -1,  0 },
11238             { +1,  0 },
11239             {  0,  0 },
11240             {  0, -1 },
11241             {  0,  0 }, { 0, 0 }, { 0, 0 },
11242             {  0, +1 }
11243           };
11244
11245         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11246         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11247
11248         change->actual_trigger_ce_value = CustomValue[xx][yy];
11249         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11250       }
11251
11252       if (change->can_change && !change_done)
11253       {
11254         ChangeDelay[x][y] = 1;
11255         ChangeEvent[x][y] = trigger_event;
11256
11257         HandleElementChange(x, y, p);
11258
11259         change_done = TRUE;
11260       }
11261 #if USE_NEW_DELAYED_ACTION
11262       else if (change->has_action)
11263       {
11264         ExecuteCustomElementAction(x, y, element, p);
11265         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11266       }
11267 #else
11268       if (change->has_action)
11269       {
11270         ExecuteCustomElementAction(x, y, element, p);
11271         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11272       }
11273 #endif
11274     }
11275   }
11276
11277   RECURSION_LOOP_DETECTION_END();
11278
11279   return change_done;
11280 }
11281
11282 static void PlayPlayerSound(struct PlayerInfo *player)
11283 {
11284   int jx = player->jx, jy = player->jy;
11285   int sound_element = player->artwork_element;
11286   int last_action = player->last_action_waiting;
11287   int action = player->action_waiting;
11288
11289   if (player->is_waiting)
11290   {
11291     if (action != last_action)
11292       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11293     else
11294       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11295   }
11296   else
11297   {
11298     if (action != last_action)
11299       StopSound(element_info[sound_element].sound[last_action]);
11300
11301     if (last_action == ACTION_SLEEPING)
11302       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11303   }
11304 }
11305
11306 static void PlayAllPlayersSound()
11307 {
11308   int i;
11309
11310   for (i = 0; i < MAX_PLAYERS; i++)
11311     if (stored_player[i].active)
11312       PlayPlayerSound(&stored_player[i]);
11313 }
11314
11315 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11316 {
11317   boolean last_waiting = player->is_waiting;
11318   int move_dir = player->MovDir;
11319
11320   player->dir_waiting = move_dir;
11321   player->last_action_waiting = player->action_waiting;
11322
11323   if (is_waiting)
11324   {
11325     if (!last_waiting)          /* not waiting -> waiting */
11326     {
11327       player->is_waiting = TRUE;
11328
11329       player->frame_counter_bored =
11330         FrameCounter +
11331         game.player_boring_delay_fixed +
11332         GetSimpleRandom(game.player_boring_delay_random);
11333       player->frame_counter_sleeping =
11334         FrameCounter +
11335         game.player_sleeping_delay_fixed +
11336         GetSimpleRandom(game.player_sleeping_delay_random);
11337
11338       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11339     }
11340
11341     if (game.player_sleeping_delay_fixed +
11342         game.player_sleeping_delay_random > 0 &&
11343         player->anim_delay_counter == 0 &&
11344         player->post_delay_counter == 0 &&
11345         FrameCounter >= player->frame_counter_sleeping)
11346       player->is_sleeping = TRUE;
11347     else if (game.player_boring_delay_fixed +
11348              game.player_boring_delay_random > 0 &&
11349              FrameCounter >= player->frame_counter_bored)
11350       player->is_bored = TRUE;
11351
11352     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11353                               player->is_bored ? ACTION_BORING :
11354                               ACTION_WAITING);
11355
11356     if (player->is_sleeping && player->use_murphy)
11357     {
11358       /* special case for sleeping Murphy when leaning against non-free tile */
11359
11360       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11361           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11362            !IS_MOVING(player->jx - 1, player->jy)))
11363         move_dir = MV_LEFT;
11364       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11365                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11366                 !IS_MOVING(player->jx + 1, player->jy)))
11367         move_dir = MV_RIGHT;
11368       else
11369         player->is_sleeping = FALSE;
11370
11371       player->dir_waiting = move_dir;
11372     }
11373
11374     if (player->is_sleeping)
11375     {
11376       if (player->num_special_action_sleeping > 0)
11377       {
11378         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11379         {
11380           int last_special_action = player->special_action_sleeping;
11381           int num_special_action = player->num_special_action_sleeping;
11382           int special_action =
11383             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11384              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11385              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11386              last_special_action + 1 : ACTION_SLEEPING);
11387           int special_graphic =
11388             el_act_dir2img(player->artwork_element, special_action, move_dir);
11389
11390           player->anim_delay_counter =
11391             graphic_info[special_graphic].anim_delay_fixed +
11392             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11393           player->post_delay_counter =
11394             graphic_info[special_graphic].post_delay_fixed +
11395             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11396
11397           player->special_action_sleeping = special_action;
11398         }
11399
11400         if (player->anim_delay_counter > 0)
11401         {
11402           player->action_waiting = player->special_action_sleeping;
11403           player->anim_delay_counter--;
11404         }
11405         else if (player->post_delay_counter > 0)
11406         {
11407           player->post_delay_counter--;
11408         }
11409       }
11410     }
11411     else if (player->is_bored)
11412     {
11413       if (player->num_special_action_bored > 0)
11414       {
11415         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11416         {
11417           int special_action =
11418             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11419           int special_graphic =
11420             el_act_dir2img(player->artwork_element, special_action, move_dir);
11421
11422           player->anim_delay_counter =
11423             graphic_info[special_graphic].anim_delay_fixed +
11424             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11425           player->post_delay_counter =
11426             graphic_info[special_graphic].post_delay_fixed +
11427             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11428
11429           player->special_action_bored = special_action;
11430         }
11431
11432         if (player->anim_delay_counter > 0)
11433         {
11434           player->action_waiting = player->special_action_bored;
11435           player->anim_delay_counter--;
11436         }
11437         else if (player->post_delay_counter > 0)
11438         {
11439           player->post_delay_counter--;
11440         }
11441       }
11442     }
11443   }
11444   else if (last_waiting)        /* waiting -> not waiting */
11445   {
11446     player->is_waiting = FALSE;
11447     player->is_bored = FALSE;
11448     player->is_sleeping = FALSE;
11449
11450     player->frame_counter_bored = -1;
11451     player->frame_counter_sleeping = -1;
11452
11453     player->anim_delay_counter = 0;
11454     player->post_delay_counter = 0;
11455
11456     player->dir_waiting = player->MovDir;
11457     player->action_waiting = ACTION_DEFAULT;
11458
11459     player->special_action_bored = ACTION_DEFAULT;
11460     player->special_action_sleeping = ACTION_DEFAULT;
11461   }
11462 }
11463
11464 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11465 {
11466   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11467   int left      = player_action & JOY_LEFT;
11468   int right     = player_action & JOY_RIGHT;
11469   int up        = player_action & JOY_UP;
11470   int down      = player_action & JOY_DOWN;
11471   int button1   = player_action & JOY_BUTTON_1;
11472   int button2   = player_action & JOY_BUTTON_2;
11473   int dx        = (left ? -1 : right ? 1 : 0);
11474   int dy        = (up   ? -1 : down  ? 1 : 0);
11475
11476   if (!player->active || tape.pausing)
11477     return 0;
11478
11479   if (player_action)
11480   {
11481     if (button1)
11482       snapped = SnapField(player, dx, dy);
11483     else
11484     {
11485       if (button2)
11486         dropped = DropElement(player);
11487
11488       moved = MovePlayer(player, dx, dy);
11489     }
11490
11491     if (tape.single_step && tape.recording && !tape.pausing)
11492     {
11493       if (button1 || (dropped && !moved))
11494       {
11495         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11496         SnapField(player, 0, 0);                /* stop snapping */
11497       }
11498     }
11499
11500     SetPlayerWaiting(player, FALSE);
11501
11502     return player_action;
11503   }
11504   else
11505   {
11506     /* no actions for this player (no input at player's configured device) */
11507
11508     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11509     SnapField(player, 0, 0);
11510     CheckGravityMovementWhenNotMoving(player);
11511
11512     if (player->MovPos == 0)
11513       SetPlayerWaiting(player, TRUE);
11514
11515     if (player->MovPos == 0)    /* needed for tape.playing */
11516       player->is_moving = FALSE;
11517
11518     player->is_dropping = FALSE;
11519     player->is_dropping_pressed = FALSE;
11520     player->drop_pressed_delay = 0;
11521
11522     return 0;
11523   }
11524 }
11525
11526 static void CheckLevelTime()
11527 {
11528   int i;
11529
11530   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11531   {
11532     if (level.native_em_level->lev->home == 0)  /* all players at home */
11533     {
11534       PlayerWins(local_player);
11535
11536       AllPlayersGone = TRUE;
11537
11538       level.native_em_level->lev->home = -1;
11539     }
11540
11541     if (level.native_em_level->ply[0]->alive == 0 &&
11542         level.native_em_level->ply[1]->alive == 0 &&
11543         level.native_em_level->ply[2]->alive == 0 &&
11544         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11545       AllPlayersGone = TRUE;
11546   }
11547
11548   if (TimeFrames >= FRAMES_PER_SECOND)
11549   {
11550     TimeFrames = 0;
11551     TapeTime++;
11552
11553     for (i = 0; i < MAX_PLAYERS; i++)
11554     {
11555       struct PlayerInfo *player = &stored_player[i];
11556
11557       if (SHIELD_ON(player))
11558       {
11559         player->shield_normal_time_left--;
11560
11561         if (player->shield_deadly_time_left > 0)
11562           player->shield_deadly_time_left--;
11563       }
11564     }
11565
11566     if (!local_player->LevelSolved && !level.use_step_counter)
11567     {
11568       TimePlayed++;
11569
11570       if (TimeLeft > 0)
11571       {
11572         TimeLeft--;
11573
11574         if (TimeLeft <= 10 && setup.time_limit)
11575           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11576
11577 #if 1
11578         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11579
11580         DisplayGameControlValues();
11581 #else
11582         DrawGameValue_Time(TimeLeft);
11583 #endif
11584
11585         if (!TimeLeft && setup.time_limit)
11586         {
11587           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11588             level.native_em_level->lev->killed_out_of_time = TRUE;
11589           else
11590             for (i = 0; i < MAX_PLAYERS; i++)
11591               KillPlayer(&stored_player[i]);
11592         }
11593       }
11594 #if 1
11595       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11596       {
11597         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11598
11599         DisplayGameControlValues();
11600       }
11601 #else
11602       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11603         DrawGameValue_Time(TimePlayed);
11604 #endif
11605
11606       level.native_em_level->lev->time =
11607         (level.time == 0 ? TimePlayed : TimeLeft);
11608     }
11609
11610     if (tape.recording || tape.playing)
11611       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11612   }
11613
11614 #if 1
11615   UpdateAndDisplayGameControlValues();
11616 #else
11617   UpdateGameDoorValues();
11618   DrawGameDoorValues();
11619 #endif
11620 }
11621
11622 void AdvanceFrameAndPlayerCounters(int player_nr)
11623 {
11624   int i;
11625
11626   /* advance frame counters (global frame counter and time frame counter) */
11627   FrameCounter++;
11628   TimeFrames++;
11629
11630   /* advance player counters (counters for move delay, move animation etc.) */
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632   {
11633     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11634     int move_delay_value = stored_player[i].move_delay_value;
11635     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11636
11637     if (!advance_player_counters)       /* not all players may be affected */
11638       continue;
11639
11640 #if USE_NEW_PLAYER_ANIM
11641     if (move_frames == 0)       /* less than one move per game frame */
11642     {
11643       int stepsize = TILEX / move_delay_value;
11644       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11645       int count = (stored_player[i].is_moving ?
11646                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11647
11648       if (count % delay == 0)
11649         move_frames = 1;
11650     }
11651 #endif
11652
11653     stored_player[i].Frame += move_frames;
11654
11655     if (stored_player[i].MovPos != 0)
11656       stored_player[i].StepFrame += move_frames;
11657
11658     if (stored_player[i].move_delay > 0)
11659       stored_player[i].move_delay--;
11660
11661     /* due to bugs in previous versions, counter must count up, not down */
11662     if (stored_player[i].push_delay != -1)
11663       stored_player[i].push_delay++;
11664
11665     if (stored_player[i].drop_delay > 0)
11666       stored_player[i].drop_delay--;
11667
11668     if (stored_player[i].is_dropping_pressed)
11669       stored_player[i].drop_pressed_delay++;
11670   }
11671 }
11672
11673 void StartGameActions(boolean init_network_game, boolean record_tape,
11674                       long random_seed)
11675 {
11676   unsigned long new_random_seed = InitRND(random_seed);
11677
11678   if (record_tape)
11679     TapeStartRecording(new_random_seed);
11680
11681 #if defined(NETWORK_AVALIABLE)
11682   if (init_network_game)
11683   {
11684     SendToServer_StartPlaying();
11685
11686     return;
11687   }
11688 #endif
11689
11690   InitGame();
11691 }
11692
11693 void GameActions()
11694 {
11695   static unsigned long game_frame_delay = 0;
11696   unsigned long game_frame_delay_value;
11697   byte *recorded_player_action;
11698   byte summarized_player_action = 0;
11699   byte tape_action[MAX_PLAYERS];
11700   int i;
11701
11702   /* detect endless loops, caused by custom element programming */
11703   if (recursion_loop_detected && recursion_loop_depth == 0)
11704   {
11705     char *message = getStringCat3("Internal Error ! Element ",
11706                                   EL_NAME(recursion_loop_element),
11707                                   " caused endless loop ! Quit the game ?");
11708
11709     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11710           EL_NAME(recursion_loop_element));
11711
11712     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11713
11714     recursion_loop_detected = FALSE;    /* if game should be continued */
11715
11716     free(message);
11717
11718     return;
11719   }
11720
11721   if (game.restart_level)
11722     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11723
11724   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11725   {
11726     if (level.native_em_level->lev->home == 0)  /* all players at home */
11727     {
11728       PlayerWins(local_player);
11729
11730       AllPlayersGone = TRUE;
11731
11732       level.native_em_level->lev->home = -1;
11733     }
11734
11735     if (level.native_em_level->ply[0]->alive == 0 &&
11736         level.native_em_level->ply[1]->alive == 0 &&
11737         level.native_em_level->ply[2]->alive == 0 &&
11738         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11739       AllPlayersGone = TRUE;
11740   }
11741
11742   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11743     GameWon();
11744
11745   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11746     TapeStop();
11747
11748   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11749     return;
11750
11751   game_frame_delay_value =
11752     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11753
11754   if (tape.playing && tape.warp_forward && !tape.pausing)
11755     game_frame_delay_value = 0;
11756
11757   /* ---------- main game synchronization point ---------- */
11758
11759   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11760
11761   if (network_playing && !network_player_action_received)
11762   {
11763     /* try to get network player actions in time */
11764
11765 #if defined(NETWORK_AVALIABLE)
11766     /* last chance to get network player actions without main loop delay */
11767     HandleNetworking();
11768 #endif
11769
11770     /* game was quit by network peer */
11771     if (game_status != GAME_MODE_PLAYING)
11772       return;
11773
11774     if (!network_player_action_received)
11775       return;           /* failed to get network player actions in time */
11776
11777     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11778   }
11779
11780   if (tape.pausing)
11781     return;
11782
11783   /* at this point we know that we really continue executing the game */
11784
11785   network_player_action_received = FALSE;
11786
11787   /* when playing tape, read previously recorded player input from tape data */
11788   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11789
11790 #if 1
11791   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11792   if (tape.pausing)
11793     return;
11794 #endif
11795
11796   if (tape.set_centered_player)
11797   {
11798     game.centered_player_nr_next = tape.centered_player_nr_next;
11799     game.set_centered_player = TRUE;
11800   }
11801
11802   for (i = 0; i < MAX_PLAYERS; i++)
11803   {
11804     summarized_player_action |= stored_player[i].action;
11805
11806     if (!network_playing)
11807       stored_player[i].effective_action = stored_player[i].action;
11808   }
11809
11810 #if defined(NETWORK_AVALIABLE)
11811   if (network_playing)
11812     SendToServer_MovePlayer(summarized_player_action);
11813 #endif
11814
11815   if (!options.network && !setup.team_mode)
11816     local_player->effective_action = summarized_player_action;
11817
11818   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11819   {
11820     for (i = 0; i < MAX_PLAYERS; i++)
11821       stored_player[i].effective_action =
11822         (i == game.centered_player_nr ? summarized_player_action : 0);
11823   }
11824
11825   if (recorded_player_action != NULL)
11826     for (i = 0; i < MAX_PLAYERS; i++)
11827       stored_player[i].effective_action = recorded_player_action[i];
11828
11829   for (i = 0; i < MAX_PLAYERS; i++)
11830   {
11831     tape_action[i] = stored_player[i].effective_action;
11832
11833     /* (this can only happen in the R'n'D game engine) */
11834     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11835       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11836   }
11837
11838   /* only record actions from input devices, but not programmed actions */
11839   if (tape.recording)
11840     TapeRecordAction(tape_action);
11841
11842   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11843   {
11844     GameActions_EM_Main();
11845   }
11846   else
11847   {
11848     GameActions_RND();
11849   }
11850 }
11851
11852 void GameActions_EM_Main()
11853 {
11854   byte effective_action[MAX_PLAYERS];
11855   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11856   int i;
11857
11858   for (i = 0; i < MAX_PLAYERS; i++)
11859     effective_action[i] = stored_player[i].effective_action;
11860
11861   GameActions_EM(effective_action, warp_mode);
11862
11863   CheckLevelTime();
11864
11865   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11866 }
11867
11868 void GameActions_RND()
11869 {
11870   int magic_wall_x = 0, magic_wall_y = 0;
11871   int i, x, y, element, graphic;
11872
11873   InitPlayfieldScanModeVars();
11874
11875 #if USE_ONE_MORE_CHANGE_PER_FRAME
11876   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11877   {
11878     SCAN_PLAYFIELD(x, y)
11879     {
11880       ChangeCount[x][y] = 0;
11881       ChangeEvent[x][y] = -1;
11882     }
11883   }
11884 #endif
11885
11886   if (game.set_centered_player)
11887   {
11888     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11889
11890     /* switching to "all players" only possible if all players fit to screen */
11891     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11892     {
11893       game.centered_player_nr_next = game.centered_player_nr;
11894       game.set_centered_player = FALSE;
11895     }
11896
11897     /* do not switch focus to non-existing (or non-active) player */
11898     if (game.centered_player_nr_next >= 0 &&
11899         !stored_player[game.centered_player_nr_next].active)
11900     {
11901       game.centered_player_nr_next = game.centered_player_nr;
11902       game.set_centered_player = FALSE;
11903     }
11904   }
11905
11906   if (game.set_centered_player &&
11907       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11908   {
11909     int sx, sy;
11910
11911     if (game.centered_player_nr_next == -1)
11912     {
11913       setScreenCenteredToAllPlayers(&sx, &sy);
11914     }
11915     else
11916     {
11917       sx = stored_player[game.centered_player_nr_next].jx;
11918       sy = stored_player[game.centered_player_nr_next].jy;
11919     }
11920
11921     game.centered_player_nr = game.centered_player_nr_next;
11922     game.set_centered_player = FALSE;
11923
11924     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11925     DrawGameDoorValues();
11926   }
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929   {
11930     int actual_player_action = stored_player[i].effective_action;
11931
11932 #if 1
11933     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11934        - rnd_equinox_tetrachloride 048
11935        - rnd_equinox_tetrachloride_ii 096
11936        - rnd_emanuel_schmieg 002
11937        - doctor_sloan_ww 001, 020
11938     */
11939     if (stored_player[i].MovPos == 0)
11940       CheckGravityMovement(&stored_player[i]);
11941 #endif
11942
11943     /* overwrite programmed action with tape action */
11944     if (stored_player[i].programmed_action)
11945       actual_player_action = stored_player[i].programmed_action;
11946
11947     PlayerActions(&stored_player[i], actual_player_action);
11948
11949     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11950   }
11951
11952   ScrollScreen(NULL, SCROLL_GO_ON);
11953
11954   /* for backwards compatibility, the following code emulates a fixed bug that
11955      occured when pushing elements (causing elements that just made their last
11956      pushing step to already (if possible) make their first falling step in the
11957      same game frame, which is bad); this code is also needed to use the famous
11958      "spring push bug" which is used in older levels and might be wanted to be
11959      used also in newer levels, but in this case the buggy pushing code is only
11960      affecting the "spring" element and no other elements */
11961
11962   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11963   {
11964     for (i = 0; i < MAX_PLAYERS; i++)
11965     {
11966       struct PlayerInfo *player = &stored_player[i];
11967       int x = player->jx;
11968       int y = player->jy;
11969
11970       if (player->active && player->is_pushing && player->is_moving &&
11971           IS_MOVING(x, y) &&
11972           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11973            Feld[x][y] == EL_SPRING))
11974       {
11975         ContinueMoving(x, y);
11976
11977         /* continue moving after pushing (this is actually a bug) */
11978         if (!IS_MOVING(x, y))
11979           Stop[x][y] = FALSE;
11980       }
11981     }
11982   }
11983
11984 #if 0
11985   debug_print_timestamp(0, "start main loop profiling");
11986 #endif
11987
11988   SCAN_PLAYFIELD(x, y)
11989   {
11990     ChangeCount[x][y] = 0;
11991     ChangeEvent[x][y] = -1;
11992
11993     /* this must be handled before main playfield loop */
11994     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11995     {
11996       MovDelay[x][y]--;
11997       if (MovDelay[x][y] <= 0)
11998         RemoveField(x, y);
11999     }
12000
12001 #if USE_NEW_SNAP_DELAY
12002     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12003     {
12004       MovDelay[x][y]--;
12005       if (MovDelay[x][y] <= 0)
12006       {
12007         RemoveField(x, y);
12008         TEST_DrawLevelField(x, y);
12009
12010         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12011       }
12012     }
12013 #endif
12014
12015 #if DEBUG
12016     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12017     {
12018       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12019       printf("GameActions(): This should never happen!\n");
12020
12021       ChangePage[x][y] = -1;
12022     }
12023 #endif
12024
12025     Stop[x][y] = FALSE;
12026     if (WasJustMoving[x][y] > 0)
12027       WasJustMoving[x][y]--;
12028     if (WasJustFalling[x][y] > 0)
12029       WasJustFalling[x][y]--;
12030     if (CheckCollision[x][y] > 0)
12031       CheckCollision[x][y]--;
12032     if (CheckImpact[x][y] > 0)
12033       CheckImpact[x][y]--;
12034
12035     GfxFrame[x][y]++;
12036
12037     /* reset finished pushing action (not done in ContinueMoving() to allow
12038        continuous pushing animation for elements with zero push delay) */
12039     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12040     {
12041       ResetGfxAnimation(x, y);
12042       TEST_DrawLevelField(x, y);
12043     }
12044
12045 #if DEBUG
12046     if (IS_BLOCKED(x, y))
12047     {
12048       int oldx, oldy;
12049
12050       Blocked2Moving(x, y, &oldx, &oldy);
12051       if (!IS_MOVING(oldx, oldy))
12052       {
12053         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12054         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12055         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12056         printf("GameActions(): This should never happen!\n");
12057       }
12058     }
12059 #endif
12060   }
12061
12062 #if 0
12063   debug_print_timestamp(0, "- time for pre-main loop:");
12064 #endif
12065
12066 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12067   SCAN_PLAYFIELD(x, y)
12068   {
12069     element = Feld[x][y];
12070     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12071
12072 #if 1
12073     {
12074 #if 1
12075       int element2 = element;
12076       int graphic2 = graphic;
12077 #else
12078       int element2 = Feld[x][y];
12079       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12080 #endif
12081       int last_gfx_frame = GfxFrame[x][y];
12082
12083       if (graphic_info[graphic2].anim_global_sync)
12084         GfxFrame[x][y] = FrameCounter;
12085       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12086         GfxFrame[x][y] = CustomValue[x][y];
12087       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12088         GfxFrame[x][y] = element_info[element2].collect_score;
12089       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12090         GfxFrame[x][y] = ChangeDelay[x][y];
12091
12092       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12093         DrawLevelGraphicAnimation(x, y, graphic2);
12094     }
12095 #else
12096     ResetGfxFrame(x, y, TRUE);
12097 #endif
12098
12099 #if 1
12100     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12101         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12102       ResetRandomAnimationValue(x, y);
12103 #endif
12104
12105 #if 1
12106     SetRandomAnimationValue(x, y);
12107 #endif
12108
12109 #if 1
12110     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12111 #endif
12112   }
12113 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12114
12115 #if 0
12116   debug_print_timestamp(0, "- time for TEST loop:     -->");
12117 #endif
12118
12119   SCAN_PLAYFIELD(x, y)
12120   {
12121     element = Feld[x][y];
12122     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12123
12124     ResetGfxFrame(x, y, TRUE);
12125
12126     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12127         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12128       ResetRandomAnimationValue(x, y);
12129
12130     SetRandomAnimationValue(x, y);
12131
12132     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12133
12134     if (IS_INACTIVE(element))
12135     {
12136       if (IS_ANIMATED(graphic))
12137         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12138
12139       continue;
12140     }
12141
12142     /* this may take place after moving, so 'element' may have changed */
12143     if (IS_CHANGING(x, y) &&
12144         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12145     {
12146       int page = element_info[element].event_page_nr[CE_DELAY];
12147
12148 #if 1
12149       HandleElementChange(x, y, page);
12150 #else
12151       if (CAN_CHANGE(element))
12152         HandleElementChange(x, y, page);
12153
12154       if (HAS_ACTION(element))
12155         ExecuteCustomElementAction(x, y, element, page);
12156 #endif
12157
12158       element = Feld[x][y];
12159       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12160     }
12161
12162 #if 0   // ---------------------------------------------------------------------
12163
12164     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12165     {
12166       StartMoving(x, y);
12167
12168       element = Feld[x][y];
12169       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12170
12171       if (IS_ANIMATED(graphic) &&
12172           !IS_MOVING(x, y) &&
12173           !Stop[x][y])
12174         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12175
12176       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12177         TEST_DrawTwinkleOnField(x, y);
12178     }
12179     else if (IS_MOVING(x, y))
12180       ContinueMoving(x, y);
12181     else
12182     {
12183       switch (element)
12184       {
12185         case EL_ACID:
12186         case EL_EXIT_OPEN:
12187         case EL_EM_EXIT_OPEN:
12188         case EL_SP_EXIT_OPEN:
12189         case EL_STEEL_EXIT_OPEN:
12190         case EL_EM_STEEL_EXIT_OPEN:
12191         case EL_SP_TERMINAL:
12192         case EL_SP_TERMINAL_ACTIVE:
12193         case EL_EXTRA_TIME:
12194         case EL_SHIELD_NORMAL:
12195         case EL_SHIELD_DEADLY:
12196           if (IS_ANIMATED(graphic))
12197             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12198           break;
12199
12200         case EL_DYNAMITE_ACTIVE:
12201         case EL_EM_DYNAMITE_ACTIVE:
12202         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12203         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12204         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12205         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12206         case EL_SP_DISK_RED_ACTIVE:
12207           CheckDynamite(x, y);
12208           break;
12209
12210         case EL_AMOEBA_GROWING:
12211           AmoebeWaechst(x, y);
12212           break;
12213
12214         case EL_AMOEBA_SHRINKING:
12215           AmoebaDisappearing(x, y);
12216           break;
12217
12218 #if !USE_NEW_AMOEBA_CODE
12219         case EL_AMOEBA_WET:
12220         case EL_AMOEBA_DRY:
12221         case EL_AMOEBA_FULL:
12222         case EL_BD_AMOEBA:
12223         case EL_EMC_DRIPPER:
12224           AmoebeAbleger(x, y);
12225           break;
12226 #endif
12227
12228         case EL_GAME_OF_LIFE:
12229         case EL_BIOMAZE:
12230           Life(x, y);
12231           break;
12232
12233         case EL_EXIT_CLOSED:
12234           CheckExit(x, y);
12235           break;
12236
12237         case EL_EM_EXIT_CLOSED:
12238           CheckExitEM(x, y);
12239           break;
12240
12241         case EL_STEEL_EXIT_CLOSED:
12242           CheckExitSteel(x, y);
12243           break;
12244
12245         case EL_EM_STEEL_EXIT_CLOSED:
12246           CheckExitSteelEM(x, y);
12247           break;
12248
12249         case EL_SP_EXIT_CLOSED:
12250           CheckExitSP(x, y);
12251           break;
12252
12253         case EL_EXPANDABLE_WALL_GROWING:
12254         case EL_EXPANDABLE_STEELWALL_GROWING:
12255           MauerWaechst(x, y);
12256           break;
12257
12258         case EL_EXPANDABLE_WALL:
12259         case EL_EXPANDABLE_WALL_HORIZONTAL:
12260         case EL_EXPANDABLE_WALL_VERTICAL:
12261         case EL_EXPANDABLE_WALL_ANY:
12262         case EL_BD_EXPANDABLE_WALL:
12263           MauerAbleger(x, y);
12264           break;
12265
12266         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12267         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12268         case EL_EXPANDABLE_STEELWALL_ANY:
12269           MauerAblegerStahl(x, y);
12270           break;
12271
12272         case EL_FLAMES:
12273           CheckForDragon(x, y);
12274           break;
12275
12276         case EL_EXPLOSION:
12277           break;
12278
12279         case EL_ELEMENT_SNAPPING:
12280         case EL_DIAGONAL_SHRINKING:
12281         case EL_DIAGONAL_GROWING:
12282         {
12283           graphic =
12284             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12285
12286           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12287           break;
12288         }
12289
12290         default:
12291           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12292             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12293           break;
12294       }
12295     }
12296
12297 #else   // ---------------------------------------------------------------------
12298
12299     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12300     {
12301       StartMoving(x, y);
12302
12303       element = Feld[x][y];
12304       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12305
12306       if (IS_ANIMATED(graphic) &&
12307           !IS_MOVING(x, y) &&
12308           !Stop[x][y])
12309         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310
12311       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12312         TEST_DrawTwinkleOnField(x, y);
12313     }
12314     else if ((element == EL_ACID ||
12315               element == EL_EXIT_OPEN ||
12316               element == EL_EM_EXIT_OPEN ||
12317               element == EL_SP_EXIT_OPEN ||
12318               element == EL_STEEL_EXIT_OPEN ||
12319               element == EL_EM_STEEL_EXIT_OPEN ||
12320               element == EL_SP_TERMINAL ||
12321               element == EL_SP_TERMINAL_ACTIVE ||
12322               element == EL_EXTRA_TIME ||
12323               element == EL_SHIELD_NORMAL ||
12324               element == EL_SHIELD_DEADLY) &&
12325              IS_ANIMATED(graphic))
12326       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12327     else if (IS_MOVING(x, y))
12328       ContinueMoving(x, y);
12329     else if (IS_ACTIVE_BOMB(element))
12330       CheckDynamite(x, y);
12331     else if (element == EL_AMOEBA_GROWING)
12332       AmoebeWaechst(x, y);
12333     else if (element == EL_AMOEBA_SHRINKING)
12334       AmoebaDisappearing(x, y);
12335
12336 #if !USE_NEW_AMOEBA_CODE
12337     else if (IS_AMOEBALIVE(element))
12338       AmoebeAbleger(x, y);
12339 #endif
12340
12341     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12342       Life(x, y);
12343     else if (element == EL_EXIT_CLOSED)
12344       CheckExit(x, y);
12345     else if (element == EL_EM_EXIT_CLOSED)
12346       CheckExitEM(x, y);
12347     else if (element == EL_STEEL_EXIT_CLOSED)
12348       CheckExitSteel(x, y);
12349     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12350       CheckExitSteelEM(x, y);
12351     else if (element == EL_SP_EXIT_CLOSED)
12352       CheckExitSP(x, y);
12353     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12354              element == EL_EXPANDABLE_STEELWALL_GROWING)
12355       MauerWaechst(x, y);
12356     else if (element == EL_EXPANDABLE_WALL ||
12357              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12358              element == EL_EXPANDABLE_WALL_VERTICAL ||
12359              element == EL_EXPANDABLE_WALL_ANY ||
12360              element == EL_BD_EXPANDABLE_WALL)
12361       MauerAbleger(x, y);
12362     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12363              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12364              element == EL_EXPANDABLE_STEELWALL_ANY)
12365       MauerAblegerStahl(x, y);
12366     else if (element == EL_FLAMES)
12367       CheckForDragon(x, y);
12368     else if (element == EL_EXPLOSION)
12369       ; /* drawing of correct explosion animation is handled separately */
12370     else if (element == EL_ELEMENT_SNAPPING ||
12371              element == EL_DIAGONAL_SHRINKING ||
12372              element == EL_DIAGONAL_GROWING)
12373     {
12374       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12375
12376       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12377     }
12378     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12379       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12380
12381 #endif  // ---------------------------------------------------------------------
12382
12383     if (IS_BELT_ACTIVE(element))
12384       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12385
12386     if (game.magic_wall_active)
12387     {
12388       int jx = local_player->jx, jy = local_player->jy;
12389
12390       /* play the element sound at the position nearest to the player */
12391       if ((element == EL_MAGIC_WALL_FULL ||
12392            element == EL_MAGIC_WALL_ACTIVE ||
12393            element == EL_MAGIC_WALL_EMPTYING ||
12394            element == EL_BD_MAGIC_WALL_FULL ||
12395            element == EL_BD_MAGIC_WALL_ACTIVE ||
12396            element == EL_BD_MAGIC_WALL_EMPTYING ||
12397            element == EL_DC_MAGIC_WALL_FULL ||
12398            element == EL_DC_MAGIC_WALL_ACTIVE ||
12399            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12400           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12401       {
12402         magic_wall_x = x;
12403         magic_wall_y = y;
12404       }
12405     }
12406   }
12407
12408 #if 0
12409   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12410 #endif
12411
12412 #if USE_NEW_AMOEBA_CODE
12413   /* new experimental amoeba growth stuff */
12414   if (!(FrameCounter % 8))
12415   {
12416     static unsigned long random = 1684108901;
12417
12418     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12419     {
12420       x = RND(lev_fieldx);
12421       y = RND(lev_fieldy);
12422       element = Feld[x][y];
12423
12424       if (!IS_PLAYER(x,y) &&
12425           (element == EL_EMPTY ||
12426            CAN_GROW_INTO(element) ||
12427            element == EL_QUICKSAND_EMPTY ||
12428            element == EL_QUICKSAND_FAST_EMPTY ||
12429            element == EL_ACID_SPLASH_LEFT ||
12430            element == EL_ACID_SPLASH_RIGHT))
12431       {
12432         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12433             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12434             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12435             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12436           Feld[x][y] = EL_AMOEBA_DROP;
12437       }
12438
12439       random = random * 129 + 1;
12440     }
12441   }
12442 #endif
12443
12444 #if 0
12445   if (game.explosions_delayed)
12446 #endif
12447   {
12448     game.explosions_delayed = FALSE;
12449
12450     SCAN_PLAYFIELD(x, y)
12451     {
12452       element = Feld[x][y];
12453
12454       if (ExplodeField[x][y])
12455         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12456       else if (element == EL_EXPLOSION)
12457         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12458
12459       ExplodeField[x][y] = EX_TYPE_NONE;
12460     }
12461
12462     game.explosions_delayed = TRUE;
12463   }
12464
12465   if (game.magic_wall_active)
12466   {
12467     if (!(game.magic_wall_time_left % 4))
12468     {
12469       int element = Feld[magic_wall_x][magic_wall_y];
12470
12471       if (element == EL_BD_MAGIC_WALL_FULL ||
12472           element == EL_BD_MAGIC_WALL_ACTIVE ||
12473           element == EL_BD_MAGIC_WALL_EMPTYING)
12474         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12475       else if (element == EL_DC_MAGIC_WALL_FULL ||
12476                element == EL_DC_MAGIC_WALL_ACTIVE ||
12477                element == EL_DC_MAGIC_WALL_EMPTYING)
12478         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12479       else
12480         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12481     }
12482
12483     if (game.magic_wall_time_left > 0)
12484     {
12485       game.magic_wall_time_left--;
12486
12487       if (!game.magic_wall_time_left)
12488       {
12489         SCAN_PLAYFIELD(x, y)
12490         {
12491           element = Feld[x][y];
12492
12493           if (element == EL_MAGIC_WALL_ACTIVE ||
12494               element == EL_MAGIC_WALL_FULL)
12495           {
12496             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12497             TEST_DrawLevelField(x, y);
12498           }
12499           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12500                    element == EL_BD_MAGIC_WALL_FULL)
12501           {
12502             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12503             TEST_DrawLevelField(x, y);
12504           }
12505           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12506                    element == EL_DC_MAGIC_WALL_FULL)
12507           {
12508             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12509             TEST_DrawLevelField(x, y);
12510           }
12511         }
12512
12513         game.magic_wall_active = FALSE;
12514       }
12515     }
12516   }
12517
12518   if (game.light_time_left > 0)
12519   {
12520     game.light_time_left--;
12521
12522     if (game.light_time_left == 0)
12523       RedrawAllLightSwitchesAndInvisibleElements();
12524   }
12525
12526   if (game.timegate_time_left > 0)
12527   {
12528     game.timegate_time_left--;
12529
12530     if (game.timegate_time_left == 0)
12531       CloseAllOpenTimegates();
12532   }
12533
12534   if (game.lenses_time_left > 0)
12535   {
12536     game.lenses_time_left--;
12537
12538     if (game.lenses_time_left == 0)
12539       RedrawAllInvisibleElementsForLenses();
12540   }
12541
12542   if (game.magnify_time_left > 0)
12543   {
12544     game.magnify_time_left--;
12545
12546     if (game.magnify_time_left == 0)
12547       RedrawAllInvisibleElementsForMagnifier();
12548   }
12549
12550   for (i = 0; i < MAX_PLAYERS; i++)
12551   {
12552     struct PlayerInfo *player = &stored_player[i];
12553
12554     if (SHIELD_ON(player))
12555     {
12556       if (player->shield_deadly_time_left)
12557         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12558       else if (player->shield_normal_time_left)
12559         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12560     }
12561   }
12562
12563 #if USE_DELAYED_GFX_REDRAW
12564   SCAN_PLAYFIELD(x, y)
12565   {
12566 #if 1
12567     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12568 #else
12569     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12570         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12571 #endif
12572     {
12573       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12574          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12575
12576       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12577         DrawLevelField(x, y);
12578
12579       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12580         DrawLevelFieldCrumbledSand(x, y);
12581
12582       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12583         DrawLevelFieldCrumbledSandNeighbours(x, y);
12584
12585       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12586         DrawTwinkleOnField(x, y);
12587     }
12588
12589     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12590   }
12591 #endif
12592
12593   CheckLevelTime();
12594
12595   DrawAllPlayers();
12596   PlayAllPlayersSound();
12597
12598   if (options.debug)                    /* calculate frames per second */
12599   {
12600     static unsigned long fps_counter = 0;
12601     static int fps_frames = 0;
12602     unsigned long fps_delay_ms = Counter() - fps_counter;
12603
12604     fps_frames++;
12605
12606     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12607     {
12608       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12609
12610       fps_frames = 0;
12611       fps_counter = Counter();
12612     }
12613
12614     redraw_mask |= REDRAW_FPS;
12615   }
12616
12617   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12618
12619   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12620   {
12621     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12622
12623     local_player->show_envelope = 0;
12624   }
12625
12626 #if 0
12627   debug_print_timestamp(0, "stop main loop profiling ");
12628   printf("----------------------------------------------------------\n");
12629 #endif
12630
12631   /* use random number generator in every frame to make it less predictable */
12632   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12633     RND(1);
12634 }
12635
12636 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12637 {
12638   int min_x = x, min_y = y, max_x = x, max_y = y;
12639   int i;
12640
12641   for (i = 0; i < MAX_PLAYERS; i++)
12642   {
12643     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12644
12645     if (!stored_player[i].active || &stored_player[i] == player)
12646       continue;
12647
12648     min_x = MIN(min_x, jx);
12649     min_y = MIN(min_y, jy);
12650     max_x = MAX(max_x, jx);
12651     max_y = MAX(max_y, jy);
12652   }
12653
12654   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12655 }
12656
12657 static boolean AllPlayersInVisibleScreen()
12658 {
12659   int i;
12660
12661   for (i = 0; i < MAX_PLAYERS; i++)
12662   {
12663     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12664
12665     if (!stored_player[i].active)
12666       continue;
12667
12668     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12669       return FALSE;
12670   }
12671
12672   return TRUE;
12673 }
12674
12675 void ScrollLevel(int dx, int dy)
12676 {
12677 #if 0
12678   /* (directly solved in BlitBitmap() now) */
12679   static Bitmap *bitmap_db_field2 = NULL;
12680   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12681   int x, y;
12682 #else
12683   int i, x, y;
12684 #endif
12685
12686 #if 0
12687   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12688   /* only horizontal XOR vertical scroll direction allowed */
12689   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12690     return;
12691 #endif
12692
12693 #if 0
12694   /* (directly solved in BlitBitmap() now) */
12695   if (bitmap_db_field2 == NULL)
12696     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12697
12698   /* needed when blitting directly to same bitmap -- should not be needed with
12699      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12700   BlitBitmap(drawto_field, bitmap_db_field2,
12701              FX + TILEX * (dx == -1) - softscroll_offset,
12702              FY + TILEY * (dy == -1) - softscroll_offset,
12703              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12704              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12705              FX + TILEX * (dx == 1) - softscroll_offset,
12706              FY + TILEY * (dy == 1) - softscroll_offset);
12707   BlitBitmap(bitmap_db_field2, drawto_field,
12708              FX + TILEX * (dx == 1) - softscroll_offset,
12709              FY + TILEY * (dy == 1) - softscroll_offset,
12710              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12711              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12712              FX + TILEX * (dx == 1) - softscroll_offset,
12713              FY + TILEY * (dy == 1) - softscroll_offset);
12714
12715 #else
12716
12717 #if 0
12718   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12719   int xsize = (BX2 - BX1 + 1);
12720   int ysize = (BY2 - BY1 + 1);
12721   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12722   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12723   int step  = (start < end ? +1 : -1);
12724
12725   for (i = start; i != end; i += step)
12726   {
12727     BlitBitmap(drawto_field, drawto_field,
12728                FX + TILEX * (dx != 0 ? i + step : 0),
12729                FY + TILEY * (dy != 0 ? i + step : 0),
12730                TILEX * (dx != 0 ? 1 : xsize),
12731                TILEY * (dy != 0 ? 1 : ysize),
12732                FX + TILEX * (dx != 0 ? i : 0),
12733                FY + TILEY * (dy != 0 ? i : 0));
12734   }
12735
12736 #else
12737
12738   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12739
12740   BlitBitmap(drawto_field, drawto_field,
12741              FX + TILEX * (dx == -1) - softscroll_offset,
12742              FY + TILEY * (dy == -1) - softscroll_offset,
12743              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12744              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12745              FX + TILEX * (dx == 1) - softscroll_offset,
12746              FY + TILEY * (dy == 1) - softscroll_offset);
12747 #endif
12748 #endif
12749
12750   if (dx != 0)
12751   {
12752     x = (dx == 1 ? BX1 : BX2);
12753     for (y = BY1; y <= BY2; y++)
12754       DrawScreenField(x, y);
12755   }
12756
12757   if (dy != 0)
12758   {
12759     y = (dy == 1 ? BY1 : BY2);
12760     for (x = BX1; x <= BX2; x++)
12761       DrawScreenField(x, y);
12762   }
12763
12764   redraw_mask |= REDRAW_FIELD;
12765 }
12766
12767 static boolean canFallDown(struct PlayerInfo *player)
12768 {
12769   int jx = player->jx, jy = player->jy;
12770
12771   return (IN_LEV_FIELD(jx, jy + 1) &&
12772           (IS_FREE(jx, jy + 1) ||
12773            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12774           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12775           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12776 }
12777
12778 static boolean canPassField(int x, int y, int move_dir)
12779 {
12780   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12781   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12782   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12783   int nextx = x + dx;
12784   int nexty = y + dy;
12785   int element = Feld[x][y];
12786
12787   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12788           !CAN_MOVE(element) &&
12789           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12790           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12791           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12792 }
12793
12794 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12795 {
12796   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12797   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12798   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12799   int newx = x + dx;
12800   int newy = y + dy;
12801
12802   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12803           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12804           (IS_DIGGABLE(Feld[newx][newy]) ||
12805            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12806            canPassField(newx, newy, move_dir)));
12807 }
12808
12809 static void CheckGravityMovement(struct PlayerInfo *player)
12810 {
12811 #if USE_PLAYER_GRAVITY
12812   if (player->gravity && !player->programmed_action)
12813 #else
12814   if (game.gravity && !player->programmed_action)
12815 #endif
12816   {
12817     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12818     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12819     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12820     int jx = player->jx, jy = player->jy;
12821     boolean player_is_moving_to_valid_field =
12822       (!player_is_snapping &&
12823        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12824         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12825     boolean player_can_fall_down = canFallDown(player);
12826
12827     if (player_can_fall_down &&
12828         !player_is_moving_to_valid_field)
12829       player->programmed_action = MV_DOWN;
12830   }
12831 }
12832
12833 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12834 {
12835   return CheckGravityMovement(player);
12836
12837 #if USE_PLAYER_GRAVITY
12838   if (player->gravity && !player->programmed_action)
12839 #else
12840   if (game.gravity && !player->programmed_action)
12841 #endif
12842   {
12843     int jx = player->jx, jy = player->jy;
12844     boolean field_under_player_is_free =
12845       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12846     boolean player_is_standing_on_valid_field =
12847       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12848        (IS_WALKABLE(Feld[jx][jy]) &&
12849         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12850
12851     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12852       player->programmed_action = MV_DOWN;
12853   }
12854 }
12855
12856 /*
12857   MovePlayerOneStep()
12858   -----------------------------------------------------------------------------
12859   dx, dy:               direction (non-diagonal) to try to move the player to
12860   real_dx, real_dy:     direction as read from input device (can be diagonal)
12861 */
12862
12863 boolean MovePlayerOneStep(struct PlayerInfo *player,
12864                           int dx, int dy, int real_dx, int real_dy)
12865 {
12866   int jx = player->jx, jy = player->jy;
12867   int new_jx = jx + dx, new_jy = jy + dy;
12868 #if !USE_FIXED_DONT_RUN_INTO
12869   int element;
12870 #endif
12871   int can_move;
12872   boolean player_can_move = !player->cannot_move;
12873
12874   if (!player->active || (!dx && !dy))
12875     return MP_NO_ACTION;
12876
12877   player->MovDir = (dx < 0 ? MV_LEFT :
12878                     dx > 0 ? MV_RIGHT :
12879                     dy < 0 ? MV_UP :
12880                     dy > 0 ? MV_DOWN :  MV_NONE);
12881
12882   if (!IN_LEV_FIELD(new_jx, new_jy))
12883     return MP_NO_ACTION;
12884
12885   if (!player_can_move)
12886   {
12887     if (player->MovPos == 0)
12888     {
12889       player->is_moving = FALSE;
12890       player->is_digging = FALSE;
12891       player->is_collecting = FALSE;
12892       player->is_snapping = FALSE;
12893       player->is_pushing = FALSE;
12894     }
12895   }
12896
12897 #if 1
12898   if (!options.network && game.centered_player_nr == -1 &&
12899       !AllPlayersInSight(player, new_jx, new_jy))
12900     return MP_NO_ACTION;
12901 #else
12902   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12903     return MP_NO_ACTION;
12904 #endif
12905
12906 #if !USE_FIXED_DONT_RUN_INTO
12907   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12908
12909   /* (moved to DigField()) */
12910   if (player_can_move && DONT_RUN_INTO(element))
12911   {
12912     if (element == EL_ACID && dx == 0 && dy == 1)
12913     {
12914       SplashAcid(new_jx, new_jy);
12915       Feld[jx][jy] = EL_PLAYER_1;
12916       InitMovingField(jx, jy, MV_DOWN);
12917       Store[jx][jy] = EL_ACID;
12918       ContinueMoving(jx, jy);
12919       BuryPlayer(player);
12920     }
12921     else
12922       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12923
12924     return MP_MOVING;
12925   }
12926 #endif
12927
12928   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12929   if (can_move != MP_MOVING)
12930     return can_move;
12931
12932   /* check if DigField() has caused relocation of the player */
12933   if (player->jx != jx || player->jy != jy)
12934     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12935
12936   StorePlayer[jx][jy] = 0;
12937   player->last_jx = jx;
12938   player->last_jy = jy;
12939   player->jx = new_jx;
12940   player->jy = new_jy;
12941   StorePlayer[new_jx][new_jy] = player->element_nr;
12942
12943   if (player->move_delay_value_next != -1)
12944   {
12945     player->move_delay_value = player->move_delay_value_next;
12946     player->move_delay_value_next = -1;
12947   }
12948
12949   player->MovPos =
12950     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12951
12952   player->step_counter++;
12953
12954   PlayerVisit[jx][jy] = FrameCounter;
12955
12956 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12957   player->is_moving = TRUE;
12958 #endif
12959
12960 #if 1
12961   /* should better be called in MovePlayer(), but this breaks some tapes */
12962   ScrollPlayer(player, SCROLL_INIT);
12963 #endif
12964
12965   return MP_MOVING;
12966 }
12967
12968 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12969 {
12970   int jx = player->jx, jy = player->jy;
12971   int old_jx = jx, old_jy = jy;
12972   int moved = MP_NO_ACTION;
12973
12974   if (!player->active)
12975     return FALSE;
12976
12977   if (!dx && !dy)
12978   {
12979     if (player->MovPos == 0)
12980     {
12981       player->is_moving = FALSE;
12982       player->is_digging = FALSE;
12983       player->is_collecting = FALSE;
12984       player->is_snapping = FALSE;
12985       player->is_pushing = FALSE;
12986     }
12987
12988     return FALSE;
12989   }
12990
12991   if (player->move_delay > 0)
12992     return FALSE;
12993
12994   player->move_delay = -1;              /* set to "uninitialized" value */
12995
12996   /* store if player is automatically moved to next field */
12997   player->is_auto_moving = (player->programmed_action != MV_NONE);
12998
12999   /* remove the last programmed player action */
13000   player->programmed_action = 0;
13001
13002   if (player->MovPos)
13003   {
13004     /* should only happen if pre-1.2 tape recordings are played */
13005     /* this is only for backward compatibility */
13006
13007     int original_move_delay_value = player->move_delay_value;
13008
13009 #if DEBUG
13010     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13011            tape.counter);
13012 #endif
13013
13014     /* scroll remaining steps with finest movement resolution */
13015     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13016
13017     while (player->MovPos)
13018     {
13019       ScrollPlayer(player, SCROLL_GO_ON);
13020       ScrollScreen(NULL, SCROLL_GO_ON);
13021
13022       AdvanceFrameAndPlayerCounters(player->index_nr);
13023
13024       DrawAllPlayers();
13025       BackToFront();
13026     }
13027
13028     player->move_delay_value = original_move_delay_value;
13029   }
13030
13031   player->is_active = FALSE;
13032
13033   if (player->last_move_dir & MV_HORIZONTAL)
13034   {
13035     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13036       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13037   }
13038   else
13039   {
13040     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13041       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13042   }
13043
13044 #if USE_FIXED_BORDER_RUNNING_GFX
13045   if (!moved && !player->is_active)
13046   {
13047     player->is_moving = FALSE;
13048     player->is_digging = FALSE;
13049     player->is_collecting = FALSE;
13050     player->is_snapping = FALSE;
13051     player->is_pushing = FALSE;
13052   }
13053 #endif
13054
13055   jx = player->jx;
13056   jy = player->jy;
13057
13058 #if 1
13059   if (moved & MP_MOVING && !ScreenMovPos &&
13060       (player->index_nr == game.centered_player_nr ||
13061        game.centered_player_nr == -1))
13062 #else
13063   if (moved & MP_MOVING && !ScreenMovPos &&
13064       (player == local_player || !options.network))
13065 #endif
13066   {
13067     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13068     int offset = game.scroll_delay_value;
13069
13070     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13071     {
13072       /* actual player has left the screen -- scroll in that direction */
13073       if (jx != old_jx)         /* player has moved horizontally */
13074         scroll_x += (jx - old_jx);
13075       else                      /* player has moved vertically */
13076         scroll_y += (jy - old_jy);
13077     }
13078     else
13079     {
13080       if (jx != old_jx)         /* player has moved horizontally */
13081       {
13082         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13083             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13084           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13085
13086         /* don't scroll over playfield boundaries */
13087         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13088           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13089
13090         /* don't scroll more than one field at a time */
13091         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13092
13093         /* don't scroll against the player's moving direction */
13094         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13095             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13096           scroll_x = old_scroll_x;
13097       }
13098       else                      /* player has moved vertically */
13099       {
13100         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13101             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13102           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13103
13104         /* don't scroll over playfield boundaries */
13105         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13106           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13107
13108         /* don't scroll more than one field at a time */
13109         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13110
13111         /* don't scroll against the player's moving direction */
13112         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13113             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13114           scroll_y = old_scroll_y;
13115       }
13116     }
13117
13118     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13119     {
13120 #if 1
13121       if (!options.network && game.centered_player_nr == -1 &&
13122           !AllPlayersInVisibleScreen())
13123       {
13124         scroll_x = old_scroll_x;
13125         scroll_y = old_scroll_y;
13126       }
13127       else
13128 #else
13129       if (!options.network && !AllPlayersInVisibleScreen())
13130       {
13131         scroll_x = old_scroll_x;
13132         scroll_y = old_scroll_y;
13133       }
13134       else
13135 #endif
13136       {
13137         ScrollScreen(player, SCROLL_INIT);
13138         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13139       }
13140     }
13141   }
13142
13143   player->StepFrame = 0;
13144
13145   if (moved & MP_MOVING)
13146   {
13147     if (old_jx != jx && old_jy == jy)
13148       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13149     else if (old_jx == jx && old_jy != jy)
13150       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13151
13152     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13153
13154     player->last_move_dir = player->MovDir;
13155     player->is_moving = TRUE;
13156     player->is_snapping = FALSE;
13157     player->is_switching = FALSE;
13158     player->is_dropping = FALSE;
13159     player->is_dropping_pressed = FALSE;
13160     player->drop_pressed_delay = 0;
13161
13162 #if 0
13163     /* should better be called here than above, but this breaks some tapes */
13164     ScrollPlayer(player, SCROLL_INIT);
13165 #endif
13166   }
13167   else
13168   {
13169     CheckGravityMovementWhenNotMoving(player);
13170
13171     player->is_moving = FALSE;
13172
13173     /* at this point, the player is allowed to move, but cannot move right now
13174        (e.g. because of something blocking the way) -- ensure that the player
13175        is also allowed to move in the next frame (in old versions before 3.1.1,
13176        the player was forced to wait again for eight frames before next try) */
13177
13178     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13179       player->move_delay = 0;   /* allow direct movement in the next frame */
13180   }
13181
13182   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13183     player->move_delay = player->move_delay_value;
13184
13185   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13186   {
13187     TestIfPlayerTouchesBadThing(jx, jy);
13188     TestIfPlayerTouchesCustomElement(jx, jy);
13189   }
13190
13191   if (!player->active)
13192     RemovePlayer(player);
13193
13194   return moved;
13195 }
13196
13197 void ScrollPlayer(struct PlayerInfo *player, int mode)
13198 {
13199   int jx = player->jx, jy = player->jy;
13200   int last_jx = player->last_jx, last_jy = player->last_jy;
13201   int move_stepsize = TILEX / player->move_delay_value;
13202
13203 #if USE_NEW_PLAYER_SPEED
13204   if (!player->active)
13205     return;
13206
13207   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13208     return;
13209 #else
13210   if (!player->active || player->MovPos == 0)
13211     return;
13212 #endif
13213
13214   if (mode == SCROLL_INIT)
13215   {
13216     player->actual_frame_counter = FrameCounter;
13217     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13218
13219     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13220         Feld[last_jx][last_jy] == EL_EMPTY)
13221     {
13222       int last_field_block_delay = 0;   /* start with no blocking at all */
13223       int block_delay_adjustment = player->block_delay_adjustment;
13224
13225       /* if player blocks last field, add delay for exactly one move */
13226       if (player->block_last_field)
13227       {
13228         last_field_block_delay += player->move_delay_value;
13229
13230         /* when blocking enabled, prevent moving up despite gravity */
13231 #if USE_PLAYER_GRAVITY
13232         if (player->gravity && player->MovDir == MV_UP)
13233           block_delay_adjustment = -1;
13234 #else
13235         if (game.gravity && player->MovDir == MV_UP)
13236           block_delay_adjustment = -1;
13237 #endif
13238       }
13239
13240       /* add block delay adjustment (also possible when not blocking) */
13241       last_field_block_delay += block_delay_adjustment;
13242
13243       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13244       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13245     }
13246
13247 #if USE_NEW_PLAYER_SPEED
13248     if (player->MovPos != 0)    /* player has not yet reached destination */
13249       return;
13250 #else
13251     return;
13252 #endif
13253   }
13254   else if (!FrameReached(&player->actual_frame_counter, 1))
13255     return;
13256
13257 #if USE_NEW_PLAYER_SPEED
13258   if (player->MovPos != 0)
13259   {
13260     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13261     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13262
13263     /* before DrawPlayer() to draw correct player graphic for this case */
13264     if (player->MovPos == 0)
13265       CheckGravityMovement(player);
13266   }
13267 #else
13268   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13269   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13270
13271   /* before DrawPlayer() to draw correct player graphic for this case */
13272   if (player->MovPos == 0)
13273     CheckGravityMovement(player);
13274 #endif
13275
13276   if (player->MovPos == 0)      /* player reached destination field */
13277   {
13278     if (player->move_delay_reset_counter > 0)
13279     {
13280       player->move_delay_reset_counter--;
13281
13282       if (player->move_delay_reset_counter == 0)
13283       {
13284         /* continue with normal speed after quickly moving through gate */
13285         HALVE_PLAYER_SPEED(player);
13286
13287         /* be able to make the next move without delay */
13288         player->move_delay = 0;
13289       }
13290     }
13291
13292     player->last_jx = jx;
13293     player->last_jy = jy;
13294
13295     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13296         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13297         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13298         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13299         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13300         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13301     {
13302       DrawPlayer(player);       /* needed here only to cleanup last field */
13303       RemovePlayer(player);
13304
13305       if (local_player->friends_still_needed == 0 ||
13306           IS_SP_ELEMENT(Feld[jx][jy]))
13307         PlayerWins(player);
13308     }
13309
13310     /* this breaks one level: "machine", level 000 */
13311     {
13312       int move_direction = player->MovDir;
13313       int enter_side = MV_DIR_OPPOSITE(move_direction);
13314       int leave_side = move_direction;
13315       int old_jx = last_jx;
13316       int old_jy = last_jy;
13317       int old_element = Feld[old_jx][old_jy];
13318       int new_element = Feld[jx][jy];
13319
13320       if (IS_CUSTOM_ELEMENT(old_element))
13321         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13322                                    CE_LEFT_BY_PLAYER,
13323                                    player->index_bit, leave_side);
13324
13325       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13326                                           CE_PLAYER_LEAVES_X,
13327                                           player->index_bit, leave_side);
13328
13329       if (IS_CUSTOM_ELEMENT(new_element))
13330         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13331                                    player->index_bit, enter_side);
13332
13333       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13334                                           CE_PLAYER_ENTERS_X,
13335                                           player->index_bit, enter_side);
13336
13337 #if USE_FIX_CE_ACTION_WITH_PLAYER
13338       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13339                                         CE_MOVE_OF_X, move_direction);
13340 #else
13341       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13342                                         CE_MOVE_OF_X, move_direction);
13343 #endif
13344     }
13345
13346     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13347     {
13348       TestIfPlayerTouchesBadThing(jx, jy);
13349       TestIfPlayerTouchesCustomElement(jx, jy);
13350
13351       /* needed because pushed element has not yet reached its destination,
13352          so it would trigger a change event at its previous field location */
13353       if (!player->is_pushing)
13354         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13355
13356       if (!player->active)
13357         RemovePlayer(player);
13358     }
13359
13360     if (!local_player->LevelSolved && level.use_step_counter)
13361     {
13362       int i;
13363
13364       TimePlayed++;
13365
13366       if (TimeLeft > 0)
13367       {
13368         TimeLeft--;
13369
13370         if (TimeLeft <= 10 && setup.time_limit)
13371           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13372
13373 #if 1
13374         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13375
13376         DisplayGameControlValues();
13377 #else
13378         DrawGameValue_Time(TimeLeft);
13379 #endif
13380
13381         if (!TimeLeft && setup.time_limit)
13382           for (i = 0; i < MAX_PLAYERS; i++)
13383             KillPlayer(&stored_player[i]);
13384       }
13385 #if 1
13386       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13387       {
13388         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13389
13390         DisplayGameControlValues();
13391       }
13392 #else
13393       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13394         DrawGameValue_Time(TimePlayed);
13395 #endif
13396     }
13397
13398     if (tape.single_step && tape.recording && !tape.pausing &&
13399         !player->programmed_action)
13400       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13401   }
13402 }
13403
13404 void ScrollScreen(struct PlayerInfo *player, int mode)
13405 {
13406   static unsigned long screen_frame_counter = 0;
13407
13408   if (mode == SCROLL_INIT)
13409   {
13410     /* set scrolling step size according to actual player's moving speed */
13411     ScrollStepSize = TILEX / player->move_delay_value;
13412
13413     screen_frame_counter = FrameCounter;
13414     ScreenMovDir = player->MovDir;
13415     ScreenMovPos = player->MovPos;
13416     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13417     return;
13418   }
13419   else if (!FrameReached(&screen_frame_counter, 1))
13420     return;
13421
13422   if (ScreenMovPos)
13423   {
13424     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13425     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13426     redraw_mask |= REDRAW_FIELD;
13427   }
13428   else
13429     ScreenMovDir = MV_NONE;
13430 }
13431
13432 void TestIfPlayerTouchesCustomElement(int x, int y)
13433 {
13434   static int xy[4][2] =
13435   {
13436     { 0, -1 },
13437     { -1, 0 },
13438     { +1, 0 },
13439     { 0, +1 }
13440   };
13441   static int trigger_sides[4][2] =
13442   {
13443     /* center side       border side */
13444     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13445     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13446     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13447     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13448   };
13449   static int touch_dir[4] =
13450   {
13451     MV_LEFT | MV_RIGHT,
13452     MV_UP   | MV_DOWN,
13453     MV_UP   | MV_DOWN,
13454     MV_LEFT | MV_RIGHT
13455   };
13456   int center_element = Feld[x][y];      /* should always be non-moving! */
13457   int i;
13458
13459   for (i = 0; i < NUM_DIRECTIONS; i++)
13460   {
13461     int xx = x + xy[i][0];
13462     int yy = y + xy[i][1];
13463     int center_side = trigger_sides[i][0];
13464     int border_side = trigger_sides[i][1];
13465     int border_element;
13466
13467     if (!IN_LEV_FIELD(xx, yy))
13468       continue;
13469
13470     if (IS_PLAYER(x, y))                /* player found at center element */
13471     {
13472       struct PlayerInfo *player = PLAYERINFO(x, y);
13473
13474       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13475         border_element = Feld[xx][yy];          /* may be moving! */
13476       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13477         border_element = Feld[xx][yy];
13478       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13479         border_element = MovingOrBlocked2Element(xx, yy);
13480       else
13481         continue;               /* center and border element do not touch */
13482
13483       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13484                                  player->index_bit, border_side);
13485       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13486                                           CE_PLAYER_TOUCHES_X,
13487                                           player->index_bit, border_side);
13488
13489 #if USE_FIX_CE_ACTION_WITH_PLAYER
13490       {
13491         /* use player element that is initially defined in the level playfield,
13492            not the player element that corresponds to the runtime player number
13493            (example: a level that contains EL_PLAYER_3 as the only player would
13494            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13495         int player_element = PLAYERINFO(x, y)->initial_element;
13496
13497         CheckElementChangeBySide(xx, yy, border_element, player_element,
13498                                  CE_TOUCHING_X, border_side);
13499       }
13500 #endif
13501     }
13502     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13503     {
13504       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13505
13506       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13507       {
13508         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13509           continue;             /* center and border element do not touch */
13510       }
13511
13512       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13513                                  player->index_bit, center_side);
13514       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13515                                           CE_PLAYER_TOUCHES_X,
13516                                           player->index_bit, center_side);
13517
13518 #if USE_FIX_CE_ACTION_WITH_PLAYER
13519       {
13520         /* use player element that is initially defined in the level playfield,
13521            not the player element that corresponds to the runtime player number
13522            (example: a level that contains EL_PLAYER_3 as the only player would
13523            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13524         int player_element = PLAYERINFO(xx, yy)->initial_element;
13525
13526         CheckElementChangeBySide(x, y, center_element, player_element,
13527                                  CE_TOUCHING_X, center_side);
13528       }
13529 #endif
13530
13531       break;
13532     }
13533   }
13534 }
13535
13536 #if USE_ELEMENT_TOUCHING_BUGFIX
13537
13538 void TestIfElementTouchesCustomElement(int x, int y)
13539 {
13540   static int xy[4][2] =
13541   {
13542     { 0, -1 },
13543     { -1, 0 },
13544     { +1, 0 },
13545     { 0, +1 }
13546   };
13547   static int trigger_sides[4][2] =
13548   {
13549     /* center side      border side */
13550     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13551     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13552     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13553     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13554   };
13555   static int touch_dir[4] =
13556   {
13557     MV_LEFT | MV_RIGHT,
13558     MV_UP   | MV_DOWN,
13559     MV_UP   | MV_DOWN,
13560     MV_LEFT | MV_RIGHT
13561   };
13562   boolean change_center_element = FALSE;
13563   int center_element = Feld[x][y];      /* should always be non-moving! */
13564   int border_element_old[NUM_DIRECTIONS];
13565   int i;
13566
13567   for (i = 0; i < NUM_DIRECTIONS; i++)
13568   {
13569     int xx = x + xy[i][0];
13570     int yy = y + xy[i][1];
13571     int border_element;
13572
13573     border_element_old[i] = -1;
13574
13575     if (!IN_LEV_FIELD(xx, yy))
13576       continue;
13577
13578     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13579       border_element = Feld[xx][yy];    /* may be moving! */
13580     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13581       border_element = Feld[xx][yy];
13582     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13583       border_element = MovingOrBlocked2Element(xx, yy);
13584     else
13585       continue;                 /* center and border element do not touch */
13586
13587     border_element_old[i] = border_element;
13588   }
13589
13590   for (i = 0; i < NUM_DIRECTIONS; i++)
13591   {
13592     int xx = x + xy[i][0];
13593     int yy = y + xy[i][1];
13594     int center_side = trigger_sides[i][0];
13595     int border_element = border_element_old[i];
13596
13597     if (border_element == -1)
13598       continue;
13599
13600     /* check for change of border element */
13601     CheckElementChangeBySide(xx, yy, border_element, center_element,
13602                              CE_TOUCHING_X, center_side);
13603   }
13604
13605   for (i = 0; i < NUM_DIRECTIONS; i++)
13606   {
13607     int xx = x + xy[i][0];
13608     int yy = y + xy[i][1];
13609     int border_side = trigger_sides[i][1];
13610     int border_element = border_element_old[i];
13611
13612     if (border_element == -1)
13613       continue;
13614
13615     /* check for change of center element (but change it only once) */
13616     if (!change_center_element)
13617       change_center_element =
13618         CheckElementChangeBySide(x, y, center_element, border_element,
13619                                  CE_TOUCHING_X, border_side);
13620
13621 #if USE_FIX_CE_ACTION_WITH_PLAYER
13622     if (IS_PLAYER(xx, yy))
13623     {
13624       /* use player element that is initially defined in the level playfield,
13625          not the player element that corresponds to the runtime player number
13626          (example: a level that contains EL_PLAYER_3 as the only player would
13627          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13628       int player_element = PLAYERINFO(xx, yy)->initial_element;
13629
13630       CheckElementChangeBySide(x, y, center_element, player_element,
13631                                CE_TOUCHING_X, border_side);
13632     }
13633 #endif
13634   }
13635 }
13636
13637 #else
13638
13639 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13640 {
13641   static int xy[4][2] =
13642   {
13643     { 0, -1 },
13644     { -1, 0 },
13645     { +1, 0 },
13646     { 0, +1 }
13647   };
13648   static int trigger_sides[4][2] =
13649   {
13650     /* center side      border side */
13651     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13652     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13653     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13654     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13655   };
13656   static int touch_dir[4] =
13657   {
13658     MV_LEFT | MV_RIGHT,
13659     MV_UP   | MV_DOWN,
13660     MV_UP   | MV_DOWN,
13661     MV_LEFT | MV_RIGHT
13662   };
13663   boolean change_center_element = FALSE;
13664   int center_element = Feld[x][y];      /* should always be non-moving! */
13665   int i;
13666
13667   for (i = 0; i < NUM_DIRECTIONS; i++)
13668   {
13669     int xx = x + xy[i][0];
13670     int yy = y + xy[i][1];
13671     int center_side = trigger_sides[i][0];
13672     int border_side = trigger_sides[i][1];
13673     int border_element;
13674
13675     if (!IN_LEV_FIELD(xx, yy))
13676       continue;
13677
13678     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13679       border_element = Feld[xx][yy];    /* may be moving! */
13680     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13681       border_element = Feld[xx][yy];
13682     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13683       border_element = MovingOrBlocked2Element(xx, yy);
13684     else
13685       continue;                 /* center and border element do not touch */
13686
13687     /* check for change of center element (but change it only once) */
13688     if (!change_center_element)
13689       change_center_element =
13690         CheckElementChangeBySide(x, y, center_element, border_element,
13691                                  CE_TOUCHING_X, border_side);
13692
13693     /* check for change of border element */
13694     CheckElementChangeBySide(xx, yy, border_element, center_element,
13695                              CE_TOUCHING_X, center_side);
13696   }
13697 }
13698
13699 #endif
13700
13701 void TestIfElementHitsCustomElement(int x, int y, int direction)
13702 {
13703   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13704   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13705   int hitx = x + dx, hity = y + dy;
13706   int hitting_element = Feld[x][y];
13707   int touched_element;
13708
13709   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13710     return;
13711
13712   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13713                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13714
13715   if (IN_LEV_FIELD(hitx, hity))
13716   {
13717     int opposite_direction = MV_DIR_OPPOSITE(direction);
13718     int hitting_side = direction;
13719     int touched_side = opposite_direction;
13720     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13721                           MovDir[hitx][hity] != direction ||
13722                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13723
13724     object_hit = TRUE;
13725
13726     if (object_hit)
13727     {
13728       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13729                                CE_HITTING_X, touched_side);
13730
13731       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13732                                CE_HIT_BY_X, hitting_side);
13733
13734       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13735                                CE_HIT_BY_SOMETHING, opposite_direction);
13736
13737 #if USE_FIX_CE_ACTION_WITH_PLAYER
13738       if (IS_PLAYER(hitx, hity))
13739       {
13740         /* use player element that is initially defined in the level playfield,
13741            not the player element that corresponds to the runtime player number
13742            (example: a level that contains EL_PLAYER_3 as the only player would
13743            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13744         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13745
13746         CheckElementChangeBySide(x, y, hitting_element, player_element,
13747                                  CE_HITTING_X, touched_side);
13748       }
13749 #endif
13750     }
13751   }
13752
13753   /* "hitting something" is also true when hitting the playfield border */
13754   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13755                            CE_HITTING_SOMETHING, direction);
13756 }
13757
13758 #if 0
13759 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13760 {
13761   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13762   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13763   int hitx = x + dx, hity = y + dy;
13764   int hitting_element = Feld[x][y];
13765   int touched_element;
13766 #if 0
13767   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13768                         !IS_FREE(hitx, hity) &&
13769                         (!IS_MOVING(hitx, hity) ||
13770                          MovDir[hitx][hity] != direction ||
13771                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13772 #endif
13773
13774   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13775     return;
13776
13777 #if 0
13778   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13779     return;
13780 #endif
13781
13782   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13783                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13784
13785   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13786                            EP_CAN_SMASH_EVERYTHING, direction);
13787
13788   if (IN_LEV_FIELD(hitx, hity))
13789   {
13790     int opposite_direction = MV_DIR_OPPOSITE(direction);
13791     int hitting_side = direction;
13792     int touched_side = opposite_direction;
13793 #if 0
13794     int touched_element = MovingOrBlocked2Element(hitx, hity);
13795 #endif
13796 #if 1
13797     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13798                           MovDir[hitx][hity] != direction ||
13799                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13800
13801     object_hit = TRUE;
13802 #endif
13803
13804     if (object_hit)
13805     {
13806       int i;
13807
13808       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13809                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13810
13811       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13812                                CE_OTHER_IS_SMASHING, touched_side);
13813
13814       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13815                                CE_OTHER_GETS_SMASHED, hitting_side);
13816     }
13817   }
13818 }
13819 #endif
13820
13821 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13822 {
13823   int i, kill_x = -1, kill_y = -1;
13824
13825   int bad_element = -1;
13826   static int test_xy[4][2] =
13827   {
13828     { 0, -1 },
13829     { -1, 0 },
13830     { +1, 0 },
13831     { 0, +1 }
13832   };
13833   static int test_dir[4] =
13834   {
13835     MV_UP,
13836     MV_LEFT,
13837     MV_RIGHT,
13838     MV_DOWN
13839   };
13840
13841   for (i = 0; i < NUM_DIRECTIONS; i++)
13842   {
13843     int test_x, test_y, test_move_dir, test_element;
13844
13845     test_x = good_x + test_xy[i][0];
13846     test_y = good_y + test_xy[i][1];
13847
13848     if (!IN_LEV_FIELD(test_x, test_y))
13849       continue;
13850
13851     test_move_dir =
13852       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13853
13854     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13855
13856     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13857        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13858     */
13859     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13860         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13861     {
13862       kill_x = test_x;
13863       kill_y = test_y;
13864       bad_element = test_element;
13865
13866       break;
13867     }
13868   }
13869
13870   if (kill_x != -1 || kill_y != -1)
13871   {
13872     if (IS_PLAYER(good_x, good_y))
13873     {
13874       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13875
13876       if (player->shield_deadly_time_left > 0 &&
13877           !IS_INDESTRUCTIBLE(bad_element))
13878         Bang(kill_x, kill_y);
13879       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13880         KillPlayer(player);
13881     }
13882     else
13883       Bang(good_x, good_y);
13884   }
13885 }
13886
13887 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13888 {
13889   int i, kill_x = -1, kill_y = -1;
13890   int bad_element = Feld[bad_x][bad_y];
13891   static int test_xy[4][2] =
13892   {
13893     { 0, -1 },
13894     { -1, 0 },
13895     { +1, 0 },
13896     { 0, +1 }
13897   };
13898   static int touch_dir[4] =
13899   {
13900     MV_LEFT | MV_RIGHT,
13901     MV_UP   | MV_DOWN,
13902     MV_UP   | MV_DOWN,
13903     MV_LEFT | MV_RIGHT
13904   };
13905   static int test_dir[4] =
13906   {
13907     MV_UP,
13908     MV_LEFT,
13909     MV_RIGHT,
13910     MV_DOWN
13911   };
13912
13913   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13914     return;
13915
13916   for (i = 0; i < NUM_DIRECTIONS; i++)
13917   {
13918     int test_x, test_y, test_move_dir, test_element;
13919
13920     test_x = bad_x + test_xy[i][0];
13921     test_y = bad_y + test_xy[i][1];
13922
13923     if (!IN_LEV_FIELD(test_x, test_y))
13924       continue;
13925
13926     test_move_dir =
13927       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13928
13929     test_element = Feld[test_x][test_y];
13930
13931     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13932        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13933     */
13934     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13935         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13936     {
13937       /* good thing is player or penguin that does not move away */
13938       if (IS_PLAYER(test_x, test_y))
13939       {
13940         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13941
13942         if (bad_element == EL_ROBOT && player->is_moving)
13943           continue;     /* robot does not kill player if he is moving */
13944
13945         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13946         {
13947           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13948             continue;           /* center and border element do not touch */
13949         }
13950
13951         kill_x = test_x;
13952         kill_y = test_y;
13953
13954         break;
13955       }
13956       else if (test_element == EL_PENGUIN)
13957       {
13958         kill_x = test_x;
13959         kill_y = test_y;
13960
13961         break;
13962       }
13963     }
13964   }
13965
13966   if (kill_x != -1 || kill_y != -1)
13967   {
13968     if (IS_PLAYER(kill_x, kill_y))
13969     {
13970       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13971
13972       if (player->shield_deadly_time_left > 0 &&
13973           !IS_INDESTRUCTIBLE(bad_element))
13974         Bang(bad_x, bad_y);
13975       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13976         KillPlayer(player);
13977     }
13978     else
13979       Bang(kill_x, kill_y);
13980   }
13981 }
13982
13983 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13984 {
13985   int bad_element = Feld[bad_x][bad_y];
13986   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13987   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13988   int test_x = bad_x + dx, test_y = bad_y + dy;
13989   int test_move_dir, test_element;
13990   int kill_x = -1, kill_y = -1;
13991
13992   if (!IN_LEV_FIELD(test_x, test_y))
13993     return;
13994
13995   test_move_dir =
13996     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13997
13998   test_element = Feld[test_x][test_y];
13999
14000   if (test_move_dir != bad_move_dir)
14001   {
14002     /* good thing can be player or penguin that does not move away */
14003     if (IS_PLAYER(test_x, test_y))
14004     {
14005       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14006
14007       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14008          player as being hit when he is moving towards the bad thing, because
14009          the "get hit by" condition would be lost after the player stops) */
14010       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14011         return;         /* player moves away from bad thing */
14012
14013       kill_x = test_x;
14014       kill_y = test_y;
14015     }
14016     else if (test_element == EL_PENGUIN)
14017     {
14018       kill_x = test_x;
14019       kill_y = test_y;
14020     }
14021   }
14022
14023   if (kill_x != -1 || kill_y != -1)
14024   {
14025     if (IS_PLAYER(kill_x, kill_y))
14026     {
14027       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14028
14029       if (player->shield_deadly_time_left > 0 &&
14030           !IS_INDESTRUCTIBLE(bad_element))
14031         Bang(bad_x, bad_y);
14032       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14033         KillPlayer(player);
14034     }
14035     else
14036       Bang(kill_x, kill_y);
14037   }
14038 }
14039
14040 void TestIfPlayerTouchesBadThing(int x, int y)
14041 {
14042   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14043 }
14044
14045 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14046 {
14047   TestIfGoodThingHitsBadThing(x, y, move_dir);
14048 }
14049
14050 void TestIfBadThingTouchesPlayer(int x, int y)
14051 {
14052   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14053 }
14054
14055 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14056 {
14057   TestIfBadThingHitsGoodThing(x, y, move_dir);
14058 }
14059
14060 void TestIfFriendTouchesBadThing(int x, int y)
14061 {
14062   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14063 }
14064
14065 void TestIfBadThingTouchesFriend(int x, int y)
14066 {
14067   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14068 }
14069
14070 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14071 {
14072   int i, kill_x = bad_x, kill_y = bad_y;
14073   static int xy[4][2] =
14074   {
14075     { 0, -1 },
14076     { -1, 0 },
14077     { +1, 0 },
14078     { 0, +1 }
14079   };
14080
14081   for (i = 0; i < NUM_DIRECTIONS; i++)
14082   {
14083     int x, y, element;
14084
14085     x = bad_x + xy[i][0];
14086     y = bad_y + xy[i][1];
14087     if (!IN_LEV_FIELD(x, y))
14088       continue;
14089
14090     element = Feld[x][y];
14091     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14092         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14093     {
14094       kill_x = x;
14095       kill_y = y;
14096       break;
14097     }
14098   }
14099
14100   if (kill_x != bad_x || kill_y != bad_y)
14101     Bang(bad_x, bad_y);
14102 }
14103
14104 void KillPlayer(struct PlayerInfo *player)
14105 {
14106   int jx = player->jx, jy = player->jy;
14107
14108   if (!player->active)
14109     return;
14110
14111   /* the following code was introduced to prevent an infinite loop when calling
14112      -> Bang()
14113      -> CheckTriggeredElementChangeExt()
14114      -> ExecuteCustomElementAction()
14115      -> KillPlayer()
14116      -> (infinitely repeating the above sequence of function calls)
14117      which occurs when killing the player while having a CE with the setting
14118      "kill player X when explosion of <player X>"; the solution using a new
14119      field "player->killed" was chosen for backwards compatibility, although
14120      clever use of the fields "player->active" etc. would probably also work */
14121 #if 1
14122   if (player->killed)
14123     return;
14124 #endif
14125
14126   player->killed = TRUE;
14127
14128   /* remove accessible field at the player's position */
14129   Feld[jx][jy] = EL_EMPTY;
14130
14131   /* deactivate shield (else Bang()/Explode() would not work right) */
14132   player->shield_normal_time_left = 0;
14133   player->shield_deadly_time_left = 0;
14134
14135   Bang(jx, jy);
14136   BuryPlayer(player);
14137 }
14138
14139 static void KillPlayerUnlessEnemyProtected(int x, int y)
14140 {
14141   if (!PLAYER_ENEMY_PROTECTED(x, y))
14142     KillPlayer(PLAYERINFO(x, y));
14143 }
14144
14145 static void KillPlayerUnlessExplosionProtected(int x, int y)
14146 {
14147   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14148     KillPlayer(PLAYERINFO(x, y));
14149 }
14150
14151 void BuryPlayer(struct PlayerInfo *player)
14152 {
14153   int jx = player->jx, jy = player->jy;
14154
14155   if (!player->active)
14156     return;
14157
14158   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14159   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14160
14161   player->GameOver = TRUE;
14162   RemovePlayer(player);
14163 }
14164
14165 void RemovePlayer(struct PlayerInfo *player)
14166 {
14167   int jx = player->jx, jy = player->jy;
14168   int i, found = FALSE;
14169
14170   player->present = FALSE;
14171   player->active = FALSE;
14172
14173   if (!ExplodeField[jx][jy])
14174     StorePlayer[jx][jy] = 0;
14175
14176   if (player->is_moving)
14177     TEST_DrawLevelField(player->last_jx, player->last_jy);
14178
14179   for (i = 0; i < MAX_PLAYERS; i++)
14180     if (stored_player[i].active)
14181       found = TRUE;
14182
14183   if (!found)
14184     AllPlayersGone = TRUE;
14185
14186   ExitX = ZX = jx;
14187   ExitY = ZY = jy;
14188 }
14189
14190 #if USE_NEW_SNAP_DELAY
14191 static void setFieldForSnapping(int x, int y, int element, int direction)
14192 {
14193   struct ElementInfo *ei = &element_info[element];
14194   int direction_bit = MV_DIR_TO_BIT(direction);
14195   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14196   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14197                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14198
14199   Feld[x][y] = EL_ELEMENT_SNAPPING;
14200   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14201
14202   ResetGfxAnimation(x, y);
14203
14204   GfxElement[x][y] = element;
14205   GfxAction[x][y] = action;
14206   GfxDir[x][y] = direction;
14207   GfxFrame[x][y] = -1;
14208 }
14209 #endif
14210
14211 /*
14212   =============================================================================
14213   checkDiagonalPushing()
14214   -----------------------------------------------------------------------------
14215   check if diagonal input device direction results in pushing of object
14216   (by checking if the alternative direction is walkable, diggable, ...)
14217   =============================================================================
14218 */
14219
14220 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14221                                     int x, int y, int real_dx, int real_dy)
14222 {
14223   int jx, jy, dx, dy, xx, yy;
14224
14225   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14226     return TRUE;
14227
14228   /* diagonal direction: check alternative direction */
14229   jx = player->jx;
14230   jy = player->jy;
14231   dx = x - jx;
14232   dy = y - jy;
14233   xx = jx + (dx == 0 ? real_dx : 0);
14234   yy = jy + (dy == 0 ? real_dy : 0);
14235
14236   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14237 }
14238
14239 /*
14240   =============================================================================
14241   DigField()
14242   -----------------------------------------------------------------------------
14243   x, y:                 field next to player (non-diagonal) to try to dig to
14244   real_dx, real_dy:     direction as read from input device (can be diagonal)
14245   =============================================================================
14246 */
14247
14248 static int DigField(struct PlayerInfo *player,
14249                     int oldx, int oldy, int x, int y,
14250                     int real_dx, int real_dy, int mode)
14251 {
14252   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14253   boolean player_was_pushing = player->is_pushing;
14254   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14255   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14256   int jx = oldx, jy = oldy;
14257   int dx = x - jx, dy = y - jy;
14258   int nextx = x + dx, nexty = y + dy;
14259   int move_direction = (dx == -1 ? MV_LEFT  :
14260                         dx == +1 ? MV_RIGHT :
14261                         dy == -1 ? MV_UP    :
14262                         dy == +1 ? MV_DOWN  : MV_NONE);
14263   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14264   int dig_side = MV_DIR_OPPOSITE(move_direction);
14265   int old_element = Feld[jx][jy];
14266 #if USE_FIXED_DONT_RUN_INTO
14267   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14268 #else
14269   int element;
14270 #endif
14271   int collect_count;
14272
14273   if (is_player)                /* function can also be called by EL_PENGUIN */
14274   {
14275     if (player->MovPos == 0)
14276     {
14277       player->is_digging = FALSE;
14278       player->is_collecting = FALSE;
14279     }
14280
14281     if (player->MovPos == 0)    /* last pushing move finished */
14282       player->is_pushing = FALSE;
14283
14284     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14285     {
14286       player->is_switching = FALSE;
14287       player->push_delay = -1;
14288
14289       return MP_NO_ACTION;
14290     }
14291   }
14292
14293 #if !USE_FIXED_DONT_RUN_INTO
14294   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14295     return MP_NO_ACTION;
14296 #endif
14297
14298   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14299     old_element = Back[jx][jy];
14300
14301   /* in case of element dropped at player position, check background */
14302   else if (Back[jx][jy] != EL_EMPTY &&
14303            game.engine_version >= VERSION_IDENT(2,2,0,0))
14304     old_element = Back[jx][jy];
14305
14306   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14307     return MP_NO_ACTION;        /* field has no opening in this direction */
14308
14309   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14310     return MP_NO_ACTION;        /* field has no opening in this direction */
14311
14312 #if USE_FIXED_DONT_RUN_INTO
14313   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14314   {
14315     SplashAcid(x, y);
14316
14317     Feld[jx][jy] = player->artwork_element;
14318     InitMovingField(jx, jy, MV_DOWN);
14319     Store[jx][jy] = EL_ACID;
14320     ContinueMoving(jx, jy);
14321     BuryPlayer(player);
14322
14323     return MP_DONT_RUN_INTO;
14324   }
14325 #endif
14326
14327 #if USE_FIXED_DONT_RUN_INTO
14328   if (player_can_move && DONT_RUN_INTO(element))
14329   {
14330     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14331
14332     return MP_DONT_RUN_INTO;
14333   }
14334 #endif
14335
14336 #if USE_FIXED_DONT_RUN_INTO
14337   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14338     return MP_NO_ACTION;
14339 #endif
14340
14341 #if !USE_FIXED_DONT_RUN_INTO
14342   element = Feld[x][y];
14343 #endif
14344
14345   collect_count = element_info[element].collect_count_initial;
14346
14347   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14348     return MP_NO_ACTION;
14349
14350   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14351     player_can_move = player_can_move_or_snap;
14352
14353   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14354       game.engine_version >= VERSION_IDENT(2,2,0,0))
14355   {
14356     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14357                                player->index_bit, dig_side);
14358     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14359                                         player->index_bit, dig_side);
14360
14361     if (element == EL_DC_LANDMINE)
14362       Bang(x, y);
14363
14364     if (Feld[x][y] != element)          /* field changed by snapping */
14365       return MP_ACTION;
14366
14367     return MP_NO_ACTION;
14368   }
14369
14370 #if USE_PLAYER_GRAVITY
14371   if (player->gravity && is_player && !player->is_auto_moving &&
14372       canFallDown(player) && move_direction != MV_DOWN &&
14373       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14374     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14375 #else
14376   if (game.gravity && is_player && !player->is_auto_moving &&
14377       canFallDown(player) && move_direction != MV_DOWN &&
14378       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14379     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14380 #endif
14381
14382   if (player_can_move &&
14383       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14384   {
14385     int sound_element = SND_ELEMENT(element);
14386     int sound_action = ACTION_WALKING;
14387
14388     if (IS_RND_GATE(element))
14389     {
14390       if (!player->key[RND_GATE_NR(element)])
14391         return MP_NO_ACTION;
14392     }
14393     else if (IS_RND_GATE_GRAY(element))
14394     {
14395       if (!player->key[RND_GATE_GRAY_NR(element)])
14396         return MP_NO_ACTION;
14397     }
14398     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14399     {
14400       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14401         return MP_NO_ACTION;
14402     }
14403     else if (element == EL_EXIT_OPEN ||
14404              element == EL_EM_EXIT_OPEN ||
14405              element == EL_STEEL_EXIT_OPEN ||
14406              element == EL_EM_STEEL_EXIT_OPEN ||
14407              element == EL_SP_EXIT_OPEN ||
14408              element == EL_SP_EXIT_OPENING)
14409     {
14410       sound_action = ACTION_PASSING;    /* player is passing exit */
14411     }
14412     else if (element == EL_EMPTY)
14413     {
14414       sound_action = ACTION_MOVING;             /* nothing to walk on */
14415     }
14416
14417     /* play sound from background or player, whatever is available */
14418     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14419       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14420     else
14421       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14422   }
14423   else if (player_can_move &&
14424            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14425   {
14426     if (!ACCESS_FROM(element, opposite_direction))
14427       return MP_NO_ACTION;      /* field not accessible from this direction */
14428
14429     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14430       return MP_NO_ACTION;
14431
14432     if (IS_EM_GATE(element))
14433     {
14434       if (!player->key[EM_GATE_NR(element)])
14435         return MP_NO_ACTION;
14436     }
14437     else if (IS_EM_GATE_GRAY(element))
14438     {
14439       if (!player->key[EM_GATE_GRAY_NR(element)])
14440         return MP_NO_ACTION;
14441     }
14442     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14443     {
14444       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14445         return MP_NO_ACTION;
14446     }
14447     else if (IS_EMC_GATE(element))
14448     {
14449       if (!player->key[EMC_GATE_NR(element)])
14450         return MP_NO_ACTION;
14451     }
14452     else if (IS_EMC_GATE_GRAY(element))
14453     {
14454       if (!player->key[EMC_GATE_GRAY_NR(element)])
14455         return MP_NO_ACTION;
14456     }
14457     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14458     {
14459       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14460         return MP_NO_ACTION;
14461     }
14462     else if (element == EL_DC_GATE_WHITE ||
14463              element == EL_DC_GATE_WHITE_GRAY ||
14464              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14465     {
14466       if (player->num_white_keys == 0)
14467         return MP_NO_ACTION;
14468
14469       player->num_white_keys--;
14470     }
14471     else if (IS_SP_PORT(element))
14472     {
14473       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14474           element == EL_SP_GRAVITY_PORT_RIGHT ||
14475           element == EL_SP_GRAVITY_PORT_UP ||
14476           element == EL_SP_GRAVITY_PORT_DOWN)
14477 #if USE_PLAYER_GRAVITY
14478         player->gravity = !player->gravity;
14479 #else
14480         game.gravity = !game.gravity;
14481 #endif
14482       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14483                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14484                element == EL_SP_GRAVITY_ON_PORT_UP ||
14485                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14486 #if USE_PLAYER_GRAVITY
14487         player->gravity = TRUE;
14488 #else
14489         game.gravity = TRUE;
14490 #endif
14491       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14492                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14493                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14494                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14495 #if USE_PLAYER_GRAVITY
14496         player->gravity = FALSE;
14497 #else
14498         game.gravity = FALSE;
14499 #endif
14500     }
14501
14502     /* automatically move to the next field with double speed */
14503     player->programmed_action = move_direction;
14504
14505     if (player->move_delay_reset_counter == 0)
14506     {
14507       player->move_delay_reset_counter = 2;     /* two double speed steps */
14508
14509       DOUBLE_PLAYER_SPEED(player);
14510     }
14511
14512     PlayLevelSoundAction(x, y, ACTION_PASSING);
14513   }
14514   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14515   {
14516     RemoveField(x, y);
14517
14518     if (mode != DF_SNAP)
14519     {
14520       GfxElement[x][y] = GFX_ELEMENT(element);
14521       player->is_digging = TRUE;
14522     }
14523
14524     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14525
14526     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14527                                         player->index_bit, dig_side);
14528
14529     if (mode == DF_SNAP)
14530     {
14531 #if USE_NEW_SNAP_DELAY
14532       if (level.block_snap_field)
14533         setFieldForSnapping(x, y, element, move_direction);
14534       else
14535         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14536 #else
14537       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14538 #endif
14539
14540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14541                                           player->index_bit, dig_side);
14542     }
14543   }
14544   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14545   {
14546     RemoveField(x, y);
14547
14548     if (is_player && mode != DF_SNAP)
14549     {
14550       GfxElement[x][y] = element;
14551       player->is_collecting = TRUE;
14552     }
14553
14554     if (element == EL_SPEED_PILL)
14555     {
14556       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14557     }
14558     else if (element == EL_EXTRA_TIME && level.time > 0)
14559     {
14560       TimeLeft += level.extra_time;
14561
14562 #if 1
14563       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14564
14565       DisplayGameControlValues();
14566 #else
14567       DrawGameValue_Time(TimeLeft);
14568 #endif
14569     }
14570     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14571     {
14572       player->shield_normal_time_left += level.shield_normal_time;
14573       if (element == EL_SHIELD_DEADLY)
14574         player->shield_deadly_time_left += level.shield_deadly_time;
14575     }
14576     else if (element == EL_DYNAMITE ||
14577              element == EL_EM_DYNAMITE ||
14578              element == EL_SP_DISK_RED)
14579     {
14580       if (player->inventory_size < MAX_INVENTORY_SIZE)
14581         player->inventory_element[player->inventory_size++] = element;
14582
14583       DrawGameDoorValues();
14584     }
14585     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14586     {
14587       player->dynabomb_count++;
14588       player->dynabombs_left++;
14589     }
14590     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14591     {
14592       player->dynabomb_size++;
14593     }
14594     else if (element == EL_DYNABOMB_INCREASE_POWER)
14595     {
14596       player->dynabomb_xl = TRUE;
14597     }
14598     else if (IS_KEY(element))
14599     {
14600       player->key[KEY_NR(element)] = TRUE;
14601
14602       DrawGameDoorValues();
14603     }
14604     else if (element == EL_DC_KEY_WHITE)
14605     {
14606       player->num_white_keys++;
14607
14608       /* display white keys? */
14609       /* DrawGameDoorValues(); */
14610     }
14611     else if (IS_ENVELOPE(element))
14612     {
14613       player->show_envelope = element;
14614     }
14615     else if (element == EL_EMC_LENSES)
14616     {
14617       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14618
14619       RedrawAllInvisibleElementsForLenses();
14620     }
14621     else if (element == EL_EMC_MAGNIFIER)
14622     {
14623       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14624
14625       RedrawAllInvisibleElementsForMagnifier();
14626     }
14627     else if (IS_DROPPABLE(element) ||
14628              IS_THROWABLE(element))     /* can be collected and dropped */
14629     {
14630       int i;
14631
14632       if (collect_count == 0)
14633         player->inventory_infinite_element = element;
14634       else
14635         for (i = 0; i < collect_count; i++)
14636           if (player->inventory_size < MAX_INVENTORY_SIZE)
14637             player->inventory_element[player->inventory_size++] = element;
14638
14639       DrawGameDoorValues();
14640     }
14641     else if (collect_count > 0)
14642     {
14643       local_player->gems_still_needed -= collect_count;
14644       if (local_player->gems_still_needed < 0)
14645         local_player->gems_still_needed = 0;
14646
14647 #if 1
14648       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14649
14650       DisplayGameControlValues();
14651 #else
14652       DrawGameValue_Emeralds(local_player->gems_still_needed);
14653 #endif
14654     }
14655
14656     RaiseScoreElement(element);
14657     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14658
14659     if (is_player)
14660       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14661                                           player->index_bit, dig_side);
14662
14663     if (mode == DF_SNAP)
14664     {
14665 #if USE_NEW_SNAP_DELAY
14666       if (level.block_snap_field)
14667         setFieldForSnapping(x, y, element, move_direction);
14668       else
14669         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14670 #else
14671       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14672 #endif
14673
14674       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14675                                           player->index_bit, dig_side);
14676     }
14677   }
14678   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14679   {
14680     if (mode == DF_SNAP && element != EL_BD_ROCK)
14681       return MP_NO_ACTION;
14682
14683     if (CAN_FALL(element) && dy)
14684       return MP_NO_ACTION;
14685
14686     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14687         !(element == EL_SPRING && level.use_spring_bug))
14688       return MP_NO_ACTION;
14689
14690     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14691         ((move_direction & MV_VERTICAL &&
14692           ((element_info[element].move_pattern & MV_LEFT &&
14693             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14694            (element_info[element].move_pattern & MV_RIGHT &&
14695             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14696          (move_direction & MV_HORIZONTAL &&
14697           ((element_info[element].move_pattern & MV_UP &&
14698             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14699            (element_info[element].move_pattern & MV_DOWN &&
14700             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14701       return MP_NO_ACTION;
14702
14703     /* do not push elements already moving away faster than player */
14704     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14705         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14706       return MP_NO_ACTION;
14707
14708     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14709     {
14710       if (player->push_delay_value == -1 || !player_was_pushing)
14711         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14712     }
14713     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14714     {
14715       if (player->push_delay_value == -1)
14716         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14717     }
14718     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14719     {
14720       if (!player->is_pushing)
14721         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14722     }
14723
14724     player->is_pushing = TRUE;
14725     player->is_active = TRUE;
14726
14727     if (!(IN_LEV_FIELD(nextx, nexty) &&
14728           (IS_FREE(nextx, nexty) ||
14729            (IS_SB_ELEMENT(element) &&
14730             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14731            (IS_CUSTOM_ELEMENT(element) &&
14732             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14733       return MP_NO_ACTION;
14734
14735     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14736       return MP_NO_ACTION;
14737
14738     if (player->push_delay == -1)       /* new pushing; restart delay */
14739       player->push_delay = 0;
14740
14741     if (player->push_delay < player->push_delay_value &&
14742         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14743         element != EL_SPRING && element != EL_BALLOON)
14744     {
14745       /* make sure that there is no move delay before next try to push */
14746       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14747         player->move_delay = 0;
14748
14749       return MP_NO_ACTION;
14750     }
14751
14752     if (IS_CUSTOM_ELEMENT(element) &&
14753         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14754     {
14755       if (!DigFieldByCE(nextx, nexty, element))
14756         return MP_NO_ACTION;
14757     }
14758
14759     if (IS_SB_ELEMENT(element))
14760     {
14761       if (element == EL_SOKOBAN_FIELD_FULL)
14762       {
14763         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14764         local_player->sokobanfields_still_needed++;
14765       }
14766
14767       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14768       {
14769         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14770         local_player->sokobanfields_still_needed--;
14771       }
14772
14773       Feld[x][y] = EL_SOKOBAN_OBJECT;
14774
14775       if (Back[x][y] == Back[nextx][nexty])
14776         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14777       else if (Back[x][y] != 0)
14778         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14779                                     ACTION_EMPTYING);
14780       else
14781         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14782                                     ACTION_FILLING);
14783
14784       if (local_player->sokobanfields_still_needed == 0 &&
14785           game.emulation == EMU_SOKOBAN)
14786       {
14787         PlayerWins(player);
14788
14789         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14790       }
14791     }
14792     else
14793       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14794
14795     InitMovingField(x, y, move_direction);
14796     GfxAction[x][y] = ACTION_PUSHING;
14797
14798     if (mode == DF_SNAP)
14799       ContinueMoving(x, y);
14800     else
14801       MovPos[x][y] = (dx != 0 ? dx : dy);
14802
14803     Pushed[x][y] = TRUE;
14804     Pushed[nextx][nexty] = TRUE;
14805
14806     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14807       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14808     else
14809       player->push_delay_value = -1;    /* get new value later */
14810
14811     /* check for element change _after_ element has been pushed */
14812     if (game.use_change_when_pushing_bug)
14813     {
14814       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14815                                  player->index_bit, dig_side);
14816       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14817                                           player->index_bit, dig_side);
14818     }
14819   }
14820   else if (IS_SWITCHABLE(element))
14821   {
14822     if (PLAYER_SWITCHING(player, x, y))
14823     {
14824       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14825                                           player->index_bit, dig_side);
14826
14827       return MP_ACTION;
14828     }
14829
14830     player->is_switching = TRUE;
14831     player->switch_x = x;
14832     player->switch_y = y;
14833
14834     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14835
14836     if (element == EL_ROBOT_WHEEL)
14837     {
14838       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14839       ZX = x;
14840       ZY = y;
14841
14842       game.robot_wheel_active = TRUE;
14843
14844       TEST_DrawLevelField(x, y);
14845     }
14846     else if (element == EL_SP_TERMINAL)
14847     {
14848       int xx, yy;
14849
14850       SCAN_PLAYFIELD(xx, yy)
14851       {
14852         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14853           Bang(xx, yy);
14854         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14855           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14856       }
14857     }
14858     else if (IS_BELT_SWITCH(element))
14859     {
14860       ToggleBeltSwitch(x, y);
14861     }
14862     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14863              element == EL_SWITCHGATE_SWITCH_DOWN ||
14864              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14865              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14866     {
14867       ToggleSwitchgateSwitch(x, y);
14868     }
14869     else if (element == EL_LIGHT_SWITCH ||
14870              element == EL_LIGHT_SWITCH_ACTIVE)
14871     {
14872       ToggleLightSwitch(x, y);
14873     }
14874     else if (element == EL_TIMEGATE_SWITCH ||
14875              element == EL_DC_TIMEGATE_SWITCH)
14876     {
14877       ActivateTimegateSwitch(x, y);
14878     }
14879     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14880              element == EL_BALLOON_SWITCH_RIGHT ||
14881              element == EL_BALLOON_SWITCH_UP    ||
14882              element == EL_BALLOON_SWITCH_DOWN  ||
14883              element == EL_BALLOON_SWITCH_NONE  ||
14884              element == EL_BALLOON_SWITCH_ANY)
14885     {
14886       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14887                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14888                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14889                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14890                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14891                              move_direction);
14892     }
14893     else if (element == EL_LAMP)
14894     {
14895       Feld[x][y] = EL_LAMP_ACTIVE;
14896       local_player->lights_still_needed--;
14897
14898       ResetGfxAnimation(x, y);
14899       TEST_DrawLevelField(x, y);
14900     }
14901     else if (element == EL_TIME_ORB_FULL)
14902     {
14903       Feld[x][y] = EL_TIME_ORB_EMPTY;
14904
14905       if (level.time > 0 || level.use_time_orb_bug)
14906       {
14907         TimeLeft += level.time_orb_time;
14908
14909 #if 1
14910         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14911
14912         DisplayGameControlValues();
14913 #else
14914         DrawGameValue_Time(TimeLeft);
14915 #endif
14916       }
14917
14918       ResetGfxAnimation(x, y);
14919       TEST_DrawLevelField(x, y);
14920     }
14921     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14922              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14923     {
14924       int xx, yy;
14925
14926       game.ball_state = !game.ball_state;
14927
14928       SCAN_PLAYFIELD(xx, yy)
14929       {
14930         int e = Feld[xx][yy];
14931
14932         if (game.ball_state)
14933         {
14934           if (e == EL_EMC_MAGIC_BALL)
14935             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14936           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14937             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14938         }
14939         else
14940         {
14941           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14942             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14943           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14944             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14945         }
14946       }
14947     }
14948
14949     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14950                                         player->index_bit, dig_side);
14951
14952     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14953                                         player->index_bit, dig_side);
14954
14955     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14956                                         player->index_bit, dig_side);
14957
14958     return MP_ACTION;
14959   }
14960   else
14961   {
14962     if (!PLAYER_SWITCHING(player, x, y))
14963     {
14964       player->is_switching = TRUE;
14965       player->switch_x = x;
14966       player->switch_y = y;
14967
14968       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14969                                  player->index_bit, dig_side);
14970       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14971                                           player->index_bit, dig_side);
14972
14973       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14974                                  player->index_bit, dig_side);
14975       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14976                                           player->index_bit, dig_side);
14977     }
14978
14979     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14980                                player->index_bit, dig_side);
14981     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14982                                         player->index_bit, dig_side);
14983
14984     return MP_NO_ACTION;
14985   }
14986
14987   player->push_delay = -1;
14988
14989   if (is_player)                /* function can also be called by EL_PENGUIN */
14990   {
14991     if (Feld[x][y] != element)          /* really digged/collected something */
14992     {
14993       player->is_collecting = !player->is_digging;
14994       player->is_active = TRUE;
14995     }
14996   }
14997
14998   return MP_MOVING;
14999 }
15000
15001 static boolean DigFieldByCE(int x, int y, int digging_element)
15002 {
15003   int element = Feld[x][y];
15004
15005   if (!IS_FREE(x, y))
15006   {
15007     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15008                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15009                   ACTION_BREAKING);
15010
15011     /* no element can dig solid indestructible elements */
15012     if (IS_INDESTRUCTIBLE(element) &&
15013         !IS_DIGGABLE(element) &&
15014         !IS_COLLECTIBLE(element))
15015       return FALSE;
15016
15017     if (AmoebaNr[x][y] &&
15018         (element == EL_AMOEBA_FULL ||
15019          element == EL_BD_AMOEBA ||
15020          element == EL_AMOEBA_GROWING))
15021     {
15022       AmoebaCnt[AmoebaNr[x][y]]--;
15023       AmoebaCnt2[AmoebaNr[x][y]]--;
15024     }
15025
15026     if (IS_MOVING(x, y))
15027       RemoveMovingField(x, y);
15028     else
15029     {
15030       RemoveField(x, y);
15031       TEST_DrawLevelField(x, y);
15032     }
15033
15034     /* if digged element was about to explode, prevent the explosion */
15035     ExplodeField[x][y] = EX_TYPE_NONE;
15036
15037     PlayLevelSoundAction(x, y, action);
15038   }
15039
15040   Store[x][y] = EL_EMPTY;
15041
15042 #if 1
15043   /* this makes it possible to leave the removed element again */
15044   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15045     Store[x][y] = element;
15046 #else
15047   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15048   {
15049     int move_leave_element = element_info[digging_element].move_leave_element;
15050
15051     /* this makes it possible to leave the removed element again */
15052     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15053                    element : move_leave_element);
15054   }
15055 #endif
15056
15057   return TRUE;
15058 }
15059
15060 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15061 {
15062   int jx = player->jx, jy = player->jy;
15063   int x = jx + dx, y = jy + dy;
15064   int snap_direction = (dx == -1 ? MV_LEFT  :
15065                         dx == +1 ? MV_RIGHT :
15066                         dy == -1 ? MV_UP    :
15067                         dy == +1 ? MV_DOWN  : MV_NONE);
15068   boolean can_continue_snapping = (level.continuous_snapping &&
15069                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15070
15071   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15072     return FALSE;
15073
15074   if (!player->active || !IN_LEV_FIELD(x, y))
15075     return FALSE;
15076
15077   if (dx && dy)
15078     return FALSE;
15079
15080   if (!dx && !dy)
15081   {
15082     if (player->MovPos == 0)
15083       player->is_pushing = FALSE;
15084
15085     player->is_snapping = FALSE;
15086
15087     if (player->MovPos == 0)
15088     {
15089       player->is_moving = FALSE;
15090       player->is_digging = FALSE;
15091       player->is_collecting = FALSE;
15092     }
15093
15094     return FALSE;
15095   }
15096
15097 #if USE_NEW_CONTINUOUS_SNAPPING
15098   /* prevent snapping with already pressed snap key when not allowed */
15099   if (player->is_snapping && !can_continue_snapping)
15100     return FALSE;
15101 #else
15102   if (player->is_snapping)
15103     return FALSE;
15104 #endif
15105
15106   player->MovDir = snap_direction;
15107
15108   if (player->MovPos == 0)
15109   {
15110     player->is_moving = FALSE;
15111     player->is_digging = FALSE;
15112     player->is_collecting = FALSE;
15113   }
15114
15115   player->is_dropping = FALSE;
15116   player->is_dropping_pressed = FALSE;
15117   player->drop_pressed_delay = 0;
15118
15119   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15120     return FALSE;
15121
15122   player->is_snapping = TRUE;
15123   player->is_active = TRUE;
15124
15125   if (player->MovPos == 0)
15126   {
15127     player->is_moving = FALSE;
15128     player->is_digging = FALSE;
15129     player->is_collecting = FALSE;
15130   }
15131
15132   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15133     TEST_DrawLevelField(player->last_jx, player->last_jy);
15134
15135   TEST_DrawLevelField(x, y);
15136
15137   return TRUE;
15138 }
15139
15140 static boolean DropElement(struct PlayerInfo *player)
15141 {
15142   int old_element, new_element;
15143   int dropx = player->jx, dropy = player->jy;
15144   int drop_direction = player->MovDir;
15145   int drop_side = drop_direction;
15146 #if 1
15147   int drop_element = get_next_dropped_element(player);
15148 #else
15149   int drop_element = (player->inventory_size > 0 ?
15150                       player->inventory_element[player->inventory_size - 1] :
15151                       player->inventory_infinite_element != EL_UNDEFINED ?
15152                       player->inventory_infinite_element :
15153                       player->dynabombs_left > 0 ?
15154                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15155                       EL_UNDEFINED);
15156 #endif
15157
15158   player->is_dropping_pressed = TRUE;
15159
15160   /* do not drop an element on top of another element; when holding drop key
15161      pressed without moving, dropped element must move away before the next
15162      element can be dropped (this is especially important if the next element
15163      is dynamite, which can be placed on background for historical reasons) */
15164   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15165     return MP_ACTION;
15166
15167   if (IS_THROWABLE(drop_element))
15168   {
15169     dropx += GET_DX_FROM_DIR(drop_direction);
15170     dropy += GET_DY_FROM_DIR(drop_direction);
15171
15172     if (!IN_LEV_FIELD(dropx, dropy))
15173       return FALSE;
15174   }
15175
15176   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15177   new_element = drop_element;           /* default: no change when dropping */
15178
15179   /* check if player is active, not moving and ready to drop */
15180   if (!player->active || player->MovPos || player->drop_delay > 0)
15181     return FALSE;
15182
15183   /* check if player has anything that can be dropped */
15184   if (new_element == EL_UNDEFINED)
15185     return FALSE;
15186
15187   /* check if drop key was pressed long enough for EM style dynamite */
15188   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15189     return FALSE;
15190
15191   /* check if anything can be dropped at the current position */
15192   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15193     return FALSE;
15194
15195   /* collected custom elements can only be dropped on empty fields */
15196   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15197     return FALSE;
15198
15199   if (old_element != EL_EMPTY)
15200     Back[dropx][dropy] = old_element;   /* store old element on this field */
15201
15202   ResetGfxAnimation(dropx, dropy);
15203   ResetRandomAnimationValue(dropx, dropy);
15204
15205   if (player->inventory_size > 0 ||
15206       player->inventory_infinite_element != EL_UNDEFINED)
15207   {
15208     if (player->inventory_size > 0)
15209     {
15210       player->inventory_size--;
15211
15212       DrawGameDoorValues();
15213
15214       if (new_element == EL_DYNAMITE)
15215         new_element = EL_DYNAMITE_ACTIVE;
15216       else if (new_element == EL_EM_DYNAMITE)
15217         new_element = EL_EM_DYNAMITE_ACTIVE;
15218       else if (new_element == EL_SP_DISK_RED)
15219         new_element = EL_SP_DISK_RED_ACTIVE;
15220     }
15221
15222     Feld[dropx][dropy] = new_element;
15223
15224     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15225       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15226                           el2img(Feld[dropx][dropy]), 0);
15227
15228     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15229
15230     /* needed if previous element just changed to "empty" in the last frame */
15231     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15232
15233     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15234                                player->index_bit, drop_side);
15235     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15236                                         CE_PLAYER_DROPS_X,
15237                                         player->index_bit, drop_side);
15238
15239     TestIfElementTouchesCustomElement(dropx, dropy);
15240   }
15241   else          /* player is dropping a dyna bomb */
15242   {
15243     player->dynabombs_left--;
15244
15245     Feld[dropx][dropy] = new_element;
15246
15247     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15248       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15249                           el2img(Feld[dropx][dropy]), 0);
15250
15251     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15252   }
15253
15254   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15255     InitField_WithBug1(dropx, dropy, FALSE);
15256
15257   new_element = Feld[dropx][dropy];     /* element might have changed */
15258
15259   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15260       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15261   {
15262     int move_direction, nextx, nexty;
15263
15264     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15265       MovDir[dropx][dropy] = drop_direction;
15266
15267     move_direction = MovDir[dropx][dropy];
15268     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15269     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15270
15271     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15272
15273 #if USE_FIX_IMPACT_COLLISION
15274     /* do not cause impact style collision by dropping elements that can fall */
15275     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15276 #else
15277     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15278 #endif
15279   }
15280
15281   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15282   player->is_dropping = TRUE;
15283
15284   player->drop_pressed_delay = 0;
15285   player->is_dropping_pressed = FALSE;
15286
15287   player->drop_x = dropx;
15288   player->drop_y = dropy;
15289
15290   return TRUE;
15291 }
15292
15293 /* ------------------------------------------------------------------------- */
15294 /* game sound playing functions                                              */
15295 /* ------------------------------------------------------------------------- */
15296
15297 static int *loop_sound_frame = NULL;
15298 static int *loop_sound_volume = NULL;
15299
15300 void InitPlayLevelSound()
15301 {
15302   int num_sounds = getSoundListSize();
15303
15304   checked_free(loop_sound_frame);
15305   checked_free(loop_sound_volume);
15306
15307   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15308   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15309 }
15310
15311 static void PlayLevelSound(int x, int y, int nr)
15312 {
15313   int sx = SCREENX(x), sy = SCREENY(y);
15314   int volume, stereo_position;
15315   int max_distance = 8;
15316   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15317
15318   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15319       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15320     return;
15321
15322   if (!IN_LEV_FIELD(x, y) ||
15323       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15324       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15325     return;
15326
15327   volume = SOUND_MAX_VOLUME;
15328
15329   if (!IN_SCR_FIELD(sx, sy))
15330   {
15331     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15332     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15333
15334     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15335   }
15336
15337   stereo_position = (SOUND_MAX_LEFT +
15338                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15339                      (SCR_FIELDX + 2 * max_distance));
15340
15341   if (IS_LOOP_SOUND(nr))
15342   {
15343     /* This assures that quieter loop sounds do not overwrite louder ones,
15344        while restarting sound volume comparison with each new game frame. */
15345
15346     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15347       return;
15348
15349     loop_sound_volume[nr] = volume;
15350     loop_sound_frame[nr] = FrameCounter;
15351   }
15352
15353   PlaySoundExt(nr, volume, stereo_position, type);
15354 }
15355
15356 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15357 {
15358   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15359                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15360                  y < LEVELY(BY1) ? LEVELY(BY1) :
15361                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15362                  sound_action);
15363 }
15364
15365 static void PlayLevelSoundAction(int x, int y, int action)
15366 {
15367   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15368 }
15369
15370 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15371 {
15372   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15373
15374   if (sound_effect != SND_UNDEFINED)
15375     PlayLevelSound(x, y, sound_effect);
15376 }
15377
15378 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15379                                               int action)
15380 {
15381   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15382
15383   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15384     PlayLevelSound(x, y, sound_effect);
15385 }
15386
15387 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15388 {
15389   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15390
15391   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15392     PlayLevelSound(x, y, sound_effect);
15393 }
15394
15395 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15396 {
15397   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15398
15399   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15400     StopSound(sound_effect);
15401 }
15402
15403 static void PlayLevelMusic()
15404 {
15405   if (levelset.music[level_nr] != MUS_UNDEFINED)
15406     PlayMusic(levelset.music[level_nr]);        /* from config file */
15407   else
15408     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15409 }
15410
15411 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15412 {
15413   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15414   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15415   int x = xx - 1 - offset;
15416   int y = yy - 1 - offset;
15417
15418   switch (sample)
15419   {
15420     case SAMPLE_blank:
15421       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15422       break;
15423
15424     case SAMPLE_roll:
15425       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15426       break;
15427
15428     case SAMPLE_stone:
15429       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15430       break;
15431
15432     case SAMPLE_nut:
15433       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15434       break;
15435
15436     case SAMPLE_crack:
15437       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15438       break;
15439
15440     case SAMPLE_bug:
15441       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15442       break;
15443
15444     case SAMPLE_tank:
15445       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15446       break;
15447
15448     case SAMPLE_android_clone:
15449       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15450       break;
15451
15452     case SAMPLE_android_move:
15453       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15454       break;
15455
15456     case SAMPLE_spring:
15457       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15458       break;
15459
15460     case SAMPLE_slurp:
15461       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15462       break;
15463
15464     case SAMPLE_eater:
15465       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15466       break;
15467
15468     case SAMPLE_eater_eat:
15469       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15470       break;
15471
15472     case SAMPLE_alien:
15473       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15474       break;
15475
15476     case SAMPLE_collect:
15477       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15478       break;
15479
15480     case SAMPLE_diamond:
15481       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15482       break;
15483
15484     case SAMPLE_squash:
15485       /* !!! CHECK THIS !!! */
15486 #if 1
15487       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15488 #else
15489       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15490 #endif
15491       break;
15492
15493     case SAMPLE_wonderfall:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15495       break;
15496
15497     case SAMPLE_drip:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15499       break;
15500
15501     case SAMPLE_push:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15503       break;
15504
15505     case SAMPLE_dirt:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15507       break;
15508
15509     case SAMPLE_acid:
15510       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15511       break;
15512
15513     case SAMPLE_ball:
15514       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15515       break;
15516
15517     case SAMPLE_grow:
15518       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15519       break;
15520
15521     case SAMPLE_wonder:
15522       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15523       break;
15524
15525     case SAMPLE_door:
15526       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15527       break;
15528
15529     case SAMPLE_exit_open:
15530       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15531       break;
15532
15533     case SAMPLE_exit_leave:
15534       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15535       break;
15536
15537     case SAMPLE_dynamite:
15538       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15539       break;
15540
15541     case SAMPLE_tick:
15542       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15543       break;
15544
15545     case SAMPLE_press:
15546       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15547       break;
15548
15549     case SAMPLE_wheel:
15550       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15551       break;
15552
15553     case SAMPLE_boom:
15554       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15555       break;
15556
15557     case SAMPLE_die:
15558       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15559       break;
15560
15561     case SAMPLE_time:
15562       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15563       break;
15564
15565     default:
15566       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15567       break;
15568   }
15569 }
15570
15571 #if 0
15572 void ChangeTime(int value)
15573 {
15574   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15575
15576   *time += value;
15577
15578   /* EMC game engine uses value from time counter of RND game engine */
15579   level.native_em_level->lev->time = *time;
15580
15581   DrawGameValue_Time(*time);
15582 }
15583
15584 void RaiseScore(int value)
15585 {
15586   /* EMC game engine and RND game engine have separate score counters */
15587   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15588                 &level.native_em_level->lev->score : &local_player->score);
15589
15590   *score += value;
15591
15592   DrawGameValue_Score(*score);
15593 }
15594 #endif
15595
15596 void RaiseScore(int value)
15597 {
15598   local_player->score += value;
15599
15600 #if 1
15601   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15602
15603   DisplayGameControlValues();
15604 #else
15605   DrawGameValue_Score(local_player->score);
15606 #endif
15607 }
15608
15609 void RaiseScoreElement(int element)
15610 {
15611   switch (element)
15612   {
15613     case EL_EMERALD:
15614     case EL_BD_DIAMOND:
15615     case EL_EMERALD_YELLOW:
15616     case EL_EMERALD_RED:
15617     case EL_EMERALD_PURPLE:
15618     case EL_SP_INFOTRON:
15619       RaiseScore(level.score[SC_EMERALD]);
15620       break;
15621     case EL_DIAMOND:
15622       RaiseScore(level.score[SC_DIAMOND]);
15623       break;
15624     case EL_CRYSTAL:
15625       RaiseScore(level.score[SC_CRYSTAL]);
15626       break;
15627     case EL_PEARL:
15628       RaiseScore(level.score[SC_PEARL]);
15629       break;
15630     case EL_BUG:
15631     case EL_BD_BUTTERFLY:
15632     case EL_SP_ELECTRON:
15633       RaiseScore(level.score[SC_BUG]);
15634       break;
15635     case EL_SPACESHIP:
15636     case EL_BD_FIREFLY:
15637     case EL_SP_SNIKSNAK:
15638       RaiseScore(level.score[SC_SPACESHIP]);
15639       break;
15640     case EL_YAMYAM:
15641     case EL_DARK_YAMYAM:
15642       RaiseScore(level.score[SC_YAMYAM]);
15643       break;
15644     case EL_ROBOT:
15645       RaiseScore(level.score[SC_ROBOT]);
15646       break;
15647     case EL_PACMAN:
15648       RaiseScore(level.score[SC_PACMAN]);
15649       break;
15650     case EL_NUT:
15651       RaiseScore(level.score[SC_NUT]);
15652       break;
15653     case EL_DYNAMITE:
15654     case EL_EM_DYNAMITE:
15655     case EL_SP_DISK_RED:
15656     case EL_DYNABOMB_INCREASE_NUMBER:
15657     case EL_DYNABOMB_INCREASE_SIZE:
15658     case EL_DYNABOMB_INCREASE_POWER:
15659       RaiseScore(level.score[SC_DYNAMITE]);
15660       break;
15661     case EL_SHIELD_NORMAL:
15662     case EL_SHIELD_DEADLY:
15663       RaiseScore(level.score[SC_SHIELD]);
15664       break;
15665     case EL_EXTRA_TIME:
15666       RaiseScore(level.extra_time_score);
15667       break;
15668     case EL_KEY_1:
15669     case EL_KEY_2:
15670     case EL_KEY_3:
15671     case EL_KEY_4:
15672     case EL_EM_KEY_1:
15673     case EL_EM_KEY_2:
15674     case EL_EM_KEY_3:
15675     case EL_EM_KEY_4:
15676     case EL_EMC_KEY_5:
15677     case EL_EMC_KEY_6:
15678     case EL_EMC_KEY_7:
15679     case EL_EMC_KEY_8:
15680     case EL_DC_KEY_WHITE:
15681       RaiseScore(level.score[SC_KEY]);
15682       break;
15683     default:
15684       RaiseScore(element_info[element].collect_score);
15685       break;
15686   }
15687 }
15688
15689 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15690 {
15691   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15692   {
15693 #if defined(NETWORK_AVALIABLE)
15694     if (options.network)
15695       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15696     else
15697 #endif
15698     {
15699       if (quick_quit)
15700       {
15701 #if 1
15702
15703 #if 1
15704         FadeSkipNextFadeIn();
15705 #else
15706         fading = fading_none;
15707 #endif
15708
15709 #else
15710         OpenDoor(DOOR_CLOSE_1);
15711 #endif
15712
15713         game_status = GAME_MODE_MAIN;
15714
15715 #if 1
15716         DrawAndFadeInMainMenu(REDRAW_FIELD);
15717 #else
15718         DrawMainMenu();
15719 #endif
15720       }
15721       else
15722       {
15723 #if 0
15724         FadeOut(REDRAW_FIELD);
15725 #endif
15726
15727         game_status = GAME_MODE_MAIN;
15728
15729         DrawAndFadeInMainMenu(REDRAW_FIELD);
15730       }
15731     }
15732   }
15733   else          /* continue playing the game */
15734   {
15735     if (tape.playing && tape.deactivate_display)
15736       TapeDeactivateDisplayOff(TRUE);
15737
15738     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15739
15740     if (tape.playing && tape.deactivate_display)
15741       TapeDeactivateDisplayOn();
15742   }
15743 }
15744
15745 void RequestQuitGame(boolean ask_if_really_quit)
15746 {
15747   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15748   boolean skip_request = AllPlayersGone || quick_quit;
15749
15750   RequestQuitGameExt(skip_request, quick_quit,
15751                      "Do you really want to quit the game ?");
15752 }
15753
15754
15755 /* ------------------------------------------------------------------------- */
15756 /* random generator functions                                                */
15757 /* ------------------------------------------------------------------------- */
15758
15759 unsigned int InitEngineRandom_RND(long seed)
15760 {
15761   game.num_random_calls = 0;
15762
15763 #if 0
15764   unsigned int rnd_seed = InitEngineRandom(seed);
15765
15766   printf("::: START RND: %d\n", rnd_seed);
15767
15768   return rnd_seed;
15769 #else
15770
15771   return InitEngineRandom(seed);
15772
15773 #endif
15774
15775 }
15776
15777 unsigned int RND(int max)
15778 {
15779   if (max > 0)
15780   {
15781     game.num_random_calls++;
15782
15783     return GetEngineRandom(max);
15784   }
15785
15786   return 0;
15787 }
15788
15789
15790 /* ------------------------------------------------------------------------- */
15791 /* game engine snapshot handling functions                                   */
15792 /* ------------------------------------------------------------------------- */
15793
15794 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15795
15796 struct EngineSnapshotInfo
15797 {
15798   /* runtime values for custom element collect score */
15799   int collect_score[NUM_CUSTOM_ELEMENTS];
15800
15801   /* runtime values for group element choice position */
15802   int choice_pos[NUM_GROUP_ELEMENTS];
15803
15804   /* runtime values for belt position animations */
15805   int belt_graphic[4 * NUM_BELT_PARTS];
15806   int belt_anim_mode[4 * NUM_BELT_PARTS];
15807 };
15808
15809 struct EngineSnapshotNodeInfo
15810 {
15811   void *buffer_orig;
15812   void *buffer_copy;
15813   int size;
15814 };
15815
15816 static struct EngineSnapshotInfo engine_snapshot_rnd;
15817 static ListNode *engine_snapshot_list = NULL;
15818 static char *snapshot_level_identifier = NULL;
15819 static int snapshot_level_nr = -1;
15820
15821 void FreeEngineSnapshot()
15822 {
15823   while (engine_snapshot_list != NULL)
15824     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15825                        checked_free);
15826
15827   setString(&snapshot_level_identifier, NULL);
15828   snapshot_level_nr = -1;
15829 }
15830
15831 static void SaveEngineSnapshotValues_RND()
15832 {
15833   static int belt_base_active_element[4] =
15834   {
15835     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15836     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15837     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15838     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15839   };
15840   int i, j;
15841
15842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15843   {
15844     int element = EL_CUSTOM_START + i;
15845
15846     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15847   }
15848
15849   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15850   {
15851     int element = EL_GROUP_START + i;
15852
15853     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15854   }
15855
15856   for (i = 0; i < 4; i++)
15857   {
15858     for (j = 0; j < NUM_BELT_PARTS; j++)
15859     {
15860       int element = belt_base_active_element[i] + j;
15861       int graphic = el2img(element);
15862       int anim_mode = graphic_info[graphic].anim_mode;
15863
15864       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15865       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15866     }
15867   }
15868 }
15869
15870 static void LoadEngineSnapshotValues_RND()
15871 {
15872   unsigned long num_random_calls = game.num_random_calls;
15873   int i, j;
15874
15875   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15876   {
15877     int element = EL_CUSTOM_START + i;
15878
15879     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15880   }
15881
15882   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15883   {
15884     int element = EL_GROUP_START + i;
15885
15886     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15887   }
15888
15889   for (i = 0; i < 4; i++)
15890   {
15891     for (j = 0; j < NUM_BELT_PARTS; j++)
15892     {
15893       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15894       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15895
15896       graphic_info[graphic].anim_mode = anim_mode;
15897     }
15898   }
15899
15900   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15901   {
15902     InitRND(tape.random_seed);
15903     for (i = 0; i < num_random_calls; i++)
15904       RND(1);
15905   }
15906
15907   if (game.num_random_calls != num_random_calls)
15908   {
15909     Error(ERR_INFO, "number of random calls out of sync");
15910     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15911     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15912     Error(ERR_EXIT, "this should not happen -- please debug");
15913   }
15914 }
15915
15916 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15917 {
15918   struct EngineSnapshotNodeInfo *bi =
15919     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15920
15921   bi->buffer_orig = buffer;
15922   bi->buffer_copy = checked_malloc(size);
15923   bi->size = size;
15924
15925   memcpy(bi->buffer_copy, buffer, size);
15926
15927   addNodeToList(&engine_snapshot_list, NULL, bi);
15928 }
15929
15930 void SaveEngineSnapshot()
15931 {
15932   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15933
15934   if (level_editor_test_game)   /* do not save snapshots from editor */
15935     return;
15936
15937   /* copy some special values to a structure better suited for the snapshot */
15938
15939   SaveEngineSnapshotValues_RND();
15940   SaveEngineSnapshotValues_EM();
15941
15942   /* save values stored in special snapshot structure */
15943
15944   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15945   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15946
15947   /* save further RND engine values */
15948
15949   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15950   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15951   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15952
15953   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15954   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15955   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15956   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15957
15958   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15959   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15960   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15961   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15962   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15963
15964   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15965   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15966   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15967
15968   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15969
15970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15971
15972   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15973   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15974
15975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15976   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15977   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15978   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15981   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15982   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15983   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15988   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15992   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15993
15994   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15996
15997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15999   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16000
16001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16003
16004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16005   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16007   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16009
16010   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16012
16013   /* save level identification information */
16014
16015   setString(&snapshot_level_identifier, leveldir_current->identifier);
16016   snapshot_level_nr = level_nr;
16017
16018 #if 0
16019   ListNode *node = engine_snapshot_list;
16020   int num_bytes = 0;
16021
16022   while (node != NULL)
16023   {
16024     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16025
16026     node = node->next;
16027   }
16028
16029   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16030 #endif
16031 }
16032
16033 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16034 {
16035   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16036 }
16037
16038 void LoadEngineSnapshot()
16039 {
16040   ListNode *node = engine_snapshot_list;
16041
16042   if (engine_snapshot_list == NULL)
16043     return;
16044
16045   while (node != NULL)
16046   {
16047     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16048
16049     node = node->next;
16050   }
16051
16052   /* restore special values from snapshot structure */
16053
16054   LoadEngineSnapshotValues_RND();
16055   LoadEngineSnapshotValues_EM();
16056 }
16057
16058 boolean CheckEngineSnapshot()
16059 {
16060   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16061           snapshot_level_nr == level_nr);
16062 }
16063
16064
16065 /* ---------- new game button stuff ---------------------------------------- */
16066
16067 /* graphic position values for game buttons */
16068 #define GAME_BUTTON_XSIZE       30
16069 #define GAME_BUTTON_YSIZE       30
16070 #define GAME_BUTTON_XPOS        5
16071 #define GAME_BUTTON_YPOS        215
16072 #define SOUND_BUTTON_XPOS       5
16073 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16074
16075 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16076 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16077 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16078 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16079 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16080 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16081
16082 static struct
16083 {
16084   int *x, *y;
16085   int gd_x, gd_y;
16086   int gadget_id;
16087   char *infotext;
16088 } gamebutton_info[NUM_GAME_BUTTONS] =
16089 {
16090 #if 1
16091   {
16092     &game.button.stop.x,        &game.button.stop.y,
16093     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16094     GAME_CTRL_ID_STOP,
16095     "stop game"
16096   },
16097   {
16098     &game.button.pause.x,       &game.button.pause.y,
16099     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16100     GAME_CTRL_ID_PAUSE,
16101     "pause game"
16102   },
16103   {
16104     &game.button.play.x,        &game.button.play.y,
16105     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16106     GAME_CTRL_ID_PLAY,
16107     "play game"
16108   },
16109   {
16110     &game.button.sound_music.x, &game.button.sound_music.y,
16111     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16112     SOUND_CTRL_ID_MUSIC,
16113     "background music on/off"
16114   },
16115   {
16116     &game.button.sound_loops.x, &game.button.sound_loops.y,
16117     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16118     SOUND_CTRL_ID_LOOPS,
16119     "sound loops on/off"
16120   },
16121   {
16122     &game.button.sound_simple.x,&game.button.sound_simple.y,
16123     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16124     SOUND_CTRL_ID_SIMPLE,
16125     "normal sounds on/off"
16126   }
16127 #else
16128   {
16129     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16130     GAME_CTRL_ID_STOP,
16131     "stop game"
16132   },
16133   {
16134     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16135     GAME_CTRL_ID_PAUSE,
16136     "pause game"
16137   },
16138   {
16139     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16140     GAME_CTRL_ID_PLAY,
16141     "play game"
16142   },
16143   {
16144     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16145     SOUND_CTRL_ID_MUSIC,
16146     "background music on/off"
16147   },
16148   {
16149     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16150     SOUND_CTRL_ID_LOOPS,
16151     "sound loops on/off"
16152   },
16153   {
16154     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16155     SOUND_CTRL_ID_SIMPLE,
16156     "normal sounds on/off"
16157   }
16158 #endif
16159 };
16160
16161 void CreateGameButtons()
16162 {
16163   int i;
16164
16165   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16166   {
16167     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16168     struct GadgetInfo *gi;
16169     int button_type;
16170     boolean checked;
16171     unsigned long event_mask;
16172     int x, y;
16173     int gd_xoffset, gd_yoffset;
16174     int gd_x1, gd_x2, gd_y1, gd_y2;
16175     int id = i;
16176
16177     x = DX + *gamebutton_info[i].x;
16178     y = DY + *gamebutton_info[i].y;
16179     gd_xoffset = gamebutton_info[i].gd_x;
16180     gd_yoffset = gamebutton_info[i].gd_y;
16181     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16182     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16183
16184     if (id == GAME_CTRL_ID_STOP ||
16185         id == GAME_CTRL_ID_PAUSE ||
16186         id == GAME_CTRL_ID_PLAY)
16187     {
16188       button_type = GD_TYPE_NORMAL_BUTTON;
16189       checked = FALSE;
16190       event_mask = GD_EVENT_RELEASED;
16191       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16192       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16193     }
16194     else
16195     {
16196       button_type = GD_TYPE_CHECK_BUTTON;
16197       checked =
16198         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16199          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16200          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16201       event_mask = GD_EVENT_PRESSED;
16202       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16203       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16204     }
16205
16206     gi = CreateGadget(GDI_CUSTOM_ID, id,
16207                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16208 #if 1
16209                       GDI_X, x,
16210                       GDI_Y, y,
16211 #else
16212                       GDI_X, DX + gd_xoffset,
16213                       GDI_Y, DY + gd_yoffset,
16214 #endif
16215                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16216                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16217                       GDI_TYPE, button_type,
16218                       GDI_STATE, GD_BUTTON_UNPRESSED,
16219                       GDI_CHECKED, checked,
16220                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16221                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16222                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16223                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16224                       GDI_DIRECT_DRAW, FALSE,
16225                       GDI_EVENT_MASK, event_mask,
16226                       GDI_CALLBACK_ACTION, HandleGameButtons,
16227                       GDI_END);
16228
16229     if (gi == NULL)
16230       Error(ERR_EXIT, "cannot create gadget");
16231
16232     game_gadget[id] = gi;
16233   }
16234 }
16235
16236 void FreeGameButtons()
16237 {
16238   int i;
16239
16240   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16241     FreeGadget(game_gadget[i]);
16242 }
16243
16244 static void MapGameButtons()
16245 {
16246   int i;
16247
16248   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16249     MapGadget(game_gadget[i]);
16250 }
16251
16252 void UnmapGameButtons()
16253 {
16254   int i;
16255
16256   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16257     UnmapGadget(game_gadget[i]);
16258 }
16259
16260 void RedrawGameButtons()
16261 {
16262   int i;
16263
16264   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16265     RedrawGadget(game_gadget[i]);
16266 }
16267
16268 static void HandleGameButtons(struct GadgetInfo *gi)
16269 {
16270   int id = gi->custom_id;
16271
16272   if (game_status != GAME_MODE_PLAYING)
16273     return;
16274
16275   switch (id)
16276   {
16277     case GAME_CTRL_ID_STOP:
16278       if (tape.playing)
16279         TapeStop();
16280       else
16281         RequestQuitGame(TRUE);
16282       break;
16283
16284     case GAME_CTRL_ID_PAUSE:
16285       if (options.network)
16286       {
16287 #if defined(NETWORK_AVALIABLE)
16288         if (tape.pausing)
16289           SendToServer_ContinuePlaying();
16290         else
16291           SendToServer_PausePlaying();
16292 #endif
16293       }
16294       else
16295         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16296       break;
16297
16298     case GAME_CTRL_ID_PLAY:
16299       if (tape.pausing)
16300       {
16301 #if defined(NETWORK_AVALIABLE)
16302         if (options.network)
16303           SendToServer_ContinuePlaying();
16304         else
16305 #endif
16306         {
16307           tape.pausing = FALSE;
16308           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16309         }
16310       }
16311       break;
16312
16313     case SOUND_CTRL_ID_MUSIC:
16314       if (setup.sound_music)
16315       { 
16316         setup.sound_music = FALSE;
16317         FadeMusic();
16318       }
16319       else if (audio.music_available)
16320       { 
16321         setup.sound = setup.sound_music = TRUE;
16322
16323         SetAudioMode(setup.sound);
16324
16325         PlayLevelMusic();
16326       }
16327       break;
16328
16329     case SOUND_CTRL_ID_LOOPS:
16330       if (setup.sound_loops)
16331         setup.sound_loops = FALSE;
16332       else if (audio.loops_available)
16333       {
16334         setup.sound = setup.sound_loops = TRUE;
16335         SetAudioMode(setup.sound);
16336       }
16337       break;
16338
16339     case SOUND_CTRL_ID_SIMPLE:
16340       if (setup.sound_simple)
16341         setup.sound_simple = FALSE;
16342       else if (audio.sound_available)
16343       {
16344         setup.sound = setup.sound_simple = TRUE;
16345         SetAudioMode(setup.sound);
16346       }
16347       break;
16348
16349     default:
16350       break;
16351   }
16352 }