rnd-20080121-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62
63 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
64
65 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 1)
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
1130 void KillPlayer(struct PlayerInfo *);
1131 void BuryPlayer(struct PlayerInfo *);
1132 void RemovePlayer(struct PlayerInfo *);
1133
1134 static int getInvisibleActiveFromInvisibleElement(int);
1135 static int getInvisibleFromInvisibleActiveElement(int);
1136
1137 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1138
1139 /* for detection of endless loops, caused by custom element programming */
1140 /* (using maximal playfield width x 10 is just a rough approximation) */
1141 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1142
1143 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1144 {                                                                       \
1145   if (recursion_loop_detected)                                          \
1146     return (rc);                                                        \
1147                                                                         \
1148   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1149   {                                                                     \
1150     recursion_loop_detected = TRUE;                                     \
1151     recursion_loop_element = (e);                                       \
1152   }                                                                     \
1153                                                                         \
1154   recursion_loop_depth++;                                               \
1155 }
1156
1157 #define RECURSION_LOOP_DETECTION_END()                                  \
1158 {                                                                       \
1159   recursion_loop_depth--;                                               \
1160 }
1161
1162 static int recursion_loop_depth;
1163 static boolean recursion_loop_detected;
1164 static boolean recursion_loop_element;
1165
1166
1167 /* ------------------------------------------------------------------------- */
1168 /* definition of elements that automatically change to other elements after  */
1169 /* a specified time, eventually calling a function when changing             */
1170 /* ------------------------------------------------------------------------- */
1171
1172 /* forward declaration for changer functions */
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260 #if 1
1261     EL_EMPTY,
1262 #else
1263     EL_EM_EXIT_CLOSED,
1264 #endif
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270   {
1271     EL_EM_STEEL_EXIT_OPENING,
1272     EL_EM_STEEL_EXIT_OPEN,
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_CLOSING,
1280 #if 1
1281     EL_STEELWALL,
1282 #else
1283     EL_EM_STEEL_EXIT_CLOSED,
1284 #endif
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_OPENING,
1292     EL_SP_EXIT_OPEN,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_CLOSING,
1300     EL_SP_EXIT_CLOSED,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_OPENING,
1308     EL_SWITCHGATE_OPEN,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_CLOSING,
1316     EL_SWITCHGATE_CLOSED,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_OPENING,
1324     EL_TIMEGATE_OPEN,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_CLOSING,
1332     EL_TIMEGATE_CLOSED,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338
1339   {
1340     EL_ACID_SPLASH_LEFT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_ACID_SPLASH_RIGHT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE,
1357     EL_SP_BUGGY_BASE_ACTIVATING,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVATING,
1365     EL_SP_BUGGY_BASE_ACTIVE,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVE,
1373     EL_SP_BUGGY_BASE,
1374     0,
1375     InitBuggyBase,
1376     WarnBuggyBase,
1377     NULL
1378   },
1379   {
1380     EL_TRAP,
1381     EL_TRAP_ACTIVE,
1382     0,
1383     InitTrap,
1384     NULL,
1385     ActivateTrap
1386   },
1387   {
1388     EL_TRAP_ACTIVE,
1389     EL_TRAP,
1390     31,
1391     NULL,
1392     ChangeActiveTrap,
1393     NULL
1394   },
1395   {
1396     EL_ROBOT_WHEEL_ACTIVE,
1397     EL_ROBOT_WHEEL,
1398     0,
1399     InitRobotWheel,
1400     RunRobotWheel,
1401     StopRobotWheel
1402   },
1403   {
1404     EL_TIMEGATE_SWITCH_ACTIVE,
1405     EL_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1413     EL_DC_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_EMC_MAGIC_BALL_ACTIVE,
1421     EL_EMC_MAGIC_BALL_ACTIVE,
1422     0,
1423     InitMagicBallDelay,
1424     NULL,
1425     ActivateMagicBall
1426   },
1427   {
1428     EL_EMC_SPRING_BUMPER_ACTIVE,
1429     EL_EMC_SPRING_BUMPER,
1430     8,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_SHRINKING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_GROWING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL,
1450   },
1451
1452   {
1453     EL_UNDEFINED,
1454     EL_UNDEFINED,
1455     -1,
1456     NULL,
1457     NULL,
1458     NULL
1459   }
1460 };
1461
1462 struct
1463 {
1464   int element;
1465   int push_delay_fixed, push_delay_random;
1466 }
1467 push_delay_list[] =
1468 {
1469   { EL_SPRING,                  0, 0 },
1470   { EL_BALLOON,                 0, 0 },
1471
1472   { EL_SOKOBAN_OBJECT,          2, 0 },
1473   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1474   { EL_SATELLITE,               2, 0 },
1475   { EL_SP_DISK_YELLOW,          2, 0 },
1476
1477   { EL_UNDEFINED,               0, 0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int move_stepsize;
1484 }
1485 move_stepsize_list[] =
1486 {
1487   { EL_AMOEBA_DROP,             2 },
1488   { EL_AMOEBA_DROPPING,         2 },
1489   { EL_QUICKSAND_FILLING,       1 },
1490   { EL_QUICKSAND_EMPTYING,      1 },
1491   { EL_QUICKSAND_FAST_FILLING,  2 },
1492   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1493   { EL_MAGIC_WALL_FILLING,      2 },
1494   { EL_MAGIC_WALL_EMPTYING,     2 },
1495   { EL_BD_MAGIC_WALL_FILLING,   2 },
1496   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1497   { EL_DC_MAGIC_WALL_FILLING,   2 },
1498   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1499
1500   { EL_UNDEFINED,               0 },
1501 };
1502
1503 struct
1504 {
1505   int element;
1506   int count;
1507 }
1508 collect_count_list[] =
1509 {
1510   { EL_EMERALD,                 1 },
1511   { EL_BD_DIAMOND,              1 },
1512   { EL_EMERALD_YELLOW,          1 },
1513   { EL_EMERALD_RED,             1 },
1514   { EL_EMERALD_PURPLE,          1 },
1515   { EL_DIAMOND,                 3 },
1516   { EL_SP_INFOTRON,             1 },
1517   { EL_PEARL,                   5 },
1518   { EL_CRYSTAL,                 8 },
1519
1520   { EL_UNDEFINED,               0 },
1521 };
1522
1523 struct
1524 {
1525   int element;
1526   int direction;
1527 }
1528 access_direction_list[] =
1529 {
1530   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1531   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1532   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1533   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1534   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1535   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1536   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1537   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1538   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1539   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1540   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1541
1542   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1543   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1544   { EL_SP_PORT_UP,                                                   MV_DOWN },
1545   { EL_SP_PORT_DOWN,                                         MV_UP           },
1546   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1547   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1548   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1550   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1551   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1552   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1553   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1554   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1555   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1556   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1557   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1558   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1559   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1560   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1561
1562   { EL_UNDEFINED,                       MV_NONE                              }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 /* static variables for playfield scan mode (scanning forward or backward) */
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite()
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars()
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   /* make sure that stepsize value is always a power of 2 */
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   /* do no immediately change move delay -- the player might just be moving */
1646   player->move_delay_value_next = move_delay;
1647
1648   /* information if player can move must be set separately */
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig()
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681   InitJoysticks();
1682 }
1683
1684 int GetElementFromGroupElement(int element)
1685 {
1686   if (IS_GROUP_ELEMENT(element))
1687   {
1688     struct ElementGroupInfo *group = element_info[element].group;
1689     int last_anim_random_frame = gfx.anim_random_frame;
1690     int element_pos;
1691
1692     if (group->choice_mode == ANIM_RANDOM)
1693       gfx.anim_random_frame = RND(group->num_elements_resolved);
1694
1695     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1696                                     group->choice_mode, 0,
1697                                     group->choice_pos);
1698
1699     if (group->choice_mode == ANIM_RANDOM)
1700       gfx.anim_random_frame = last_anim_random_frame;
1701
1702     group->choice_pos++;
1703
1704     element = group->element_resolved[element_pos];
1705   }
1706
1707   return element;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     /* ---------- initialize player's last field block delay --------------- */
1747
1748     /* always start with reliable default value (no adjustment needed) */
1749     player->block_delay_adjustment = 0;
1750
1751     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     /* special case 2: in game engines before 3.1.1, blocking was different */
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!options.network || player->connected)
1760     {
1761       player->active = TRUE;
1762
1763       /* remove potentially duplicate players */
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769       if (options.debug)
1770       {
1771         printf("Player %d activated.\n", player->element_nr);
1772         printf("[Local player is %d and currently %s.]\n",
1773                local_player->element_nr,
1774                local_player->active ? "active" : "not active");
1775       }
1776     }
1777
1778     Feld[x][y] = EL_EMPTY;
1779
1780     player->jx = player->last_jx = x;
1781     player->jy = player->last_jy = y;
1782   }
1783 }
1784
1785 static void InitField(int x, int y, boolean init_game)
1786 {
1787   int element = Feld[x][y];
1788
1789   switch (element)
1790   {
1791     case EL_SP_MURPHY:
1792     case EL_PLAYER_1:
1793     case EL_PLAYER_2:
1794     case EL_PLAYER_3:
1795     case EL_PLAYER_4:
1796       InitPlayerField(x, y, element, init_game);
1797       break;
1798
1799     case EL_SOKOBAN_FIELD_PLAYER:
1800       element = Feld[x][y] = EL_PLAYER_1;
1801       InitField(x, y, init_game);
1802
1803       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1804       InitField(x, y, init_game);
1805       break;
1806
1807     case EL_SOKOBAN_FIELD_EMPTY:
1808       local_player->sokobanfields_still_needed++;
1809       break;
1810
1811     case EL_STONEBLOCK:
1812       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1813         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1814       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1815         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1816       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1817         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1818       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1819         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1820       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1821         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1822       break;
1823
1824     case EL_BUG:
1825     case EL_BUG_RIGHT:
1826     case EL_BUG_UP:
1827     case EL_BUG_LEFT:
1828     case EL_BUG_DOWN:
1829     case EL_SPACESHIP:
1830     case EL_SPACESHIP_RIGHT:
1831     case EL_SPACESHIP_UP:
1832     case EL_SPACESHIP_LEFT:
1833     case EL_SPACESHIP_DOWN:
1834     case EL_BD_BUTTERFLY:
1835     case EL_BD_BUTTERFLY_RIGHT:
1836     case EL_BD_BUTTERFLY_UP:
1837     case EL_BD_BUTTERFLY_LEFT:
1838     case EL_BD_BUTTERFLY_DOWN:
1839     case EL_BD_FIREFLY:
1840     case EL_BD_FIREFLY_RIGHT:
1841     case EL_BD_FIREFLY_UP:
1842     case EL_BD_FIREFLY_LEFT:
1843     case EL_BD_FIREFLY_DOWN:
1844     case EL_PACMAN_RIGHT:
1845     case EL_PACMAN_UP:
1846     case EL_PACMAN_LEFT:
1847     case EL_PACMAN_DOWN:
1848     case EL_YAMYAM:
1849     case EL_YAMYAM_LEFT:
1850     case EL_YAMYAM_RIGHT:
1851     case EL_YAMYAM_UP:
1852     case EL_YAMYAM_DOWN:
1853     case EL_DARK_YAMYAM:
1854     case EL_ROBOT:
1855     case EL_PACMAN:
1856     case EL_SP_SNIKSNAK:
1857     case EL_SP_ELECTRON:
1858     case EL_MOLE:
1859     case EL_MOLE_LEFT:
1860     case EL_MOLE_RIGHT:
1861     case EL_MOLE_UP:
1862     case EL_MOLE_DOWN:
1863       InitMovDir(x, y);
1864       break;
1865
1866     case EL_AMOEBA_FULL:
1867     case EL_BD_AMOEBA:
1868       InitAmoebaNr(x, y);
1869       break;
1870
1871     case EL_AMOEBA_DROP:
1872       if (y == lev_fieldy - 1)
1873       {
1874         Feld[x][y] = EL_AMOEBA_GROWING;
1875         Store[x][y] = EL_AMOEBA_WET;
1876       }
1877       break;
1878
1879     case EL_DYNAMITE_ACTIVE:
1880     case EL_SP_DISK_RED_ACTIVE:
1881     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1882     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1883     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1884     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1885       MovDelay[x][y] = 96;
1886       break;
1887
1888     case EL_EM_DYNAMITE_ACTIVE:
1889       MovDelay[x][y] = 32;
1890       break;
1891
1892     case EL_LAMP:
1893       local_player->lights_still_needed++;
1894       break;
1895
1896     case EL_PENGUIN:
1897       local_player->friends_still_needed++;
1898       break;
1899
1900     case EL_PIG:
1901     case EL_DRAGON:
1902       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1903       break;
1904
1905     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1906     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1907     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1908     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1909     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1910     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1911     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1912     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1913     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1914     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1915     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1916     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1917       if (init_game)
1918       {
1919         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1920         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1921         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1922
1923         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1924         {
1925           game.belt_dir[belt_nr] = belt_dir;
1926           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1927         }
1928         else    /* more than one switch -- set it like the first switch */
1929         {
1930           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1931         }
1932       }
1933       break;
1934
1935 #if !USE_BOTH_SWITCHGATE_SWITCHES
1936     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1937       if (init_game)
1938         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1939       break;
1940
1941     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1942       if (init_game)
1943         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1944       break;
1945 #endif
1946
1947     case EL_LIGHT_SWITCH_ACTIVE:
1948       if (init_game)
1949         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1950       break;
1951
1952     case EL_INVISIBLE_STEELWALL:
1953     case EL_INVISIBLE_WALL:
1954     case EL_INVISIBLE_SAND:
1955       if (game.light_time_left > 0 ||
1956           game.lenses_time_left > 0)
1957         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1958       break;
1959
1960     case EL_EMC_MAGIC_BALL:
1961       if (game.ball_state)
1962         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL_SWITCH:
1966       if (game.ball_state)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1968       break;
1969
1970     case EL_TRIGGER_PLAYER:
1971     case EL_TRIGGER_ELEMENT:
1972     case EL_TRIGGER_CE_VALUE:
1973     case EL_TRIGGER_CE_SCORE:
1974     case EL_SELF:
1975     case EL_ANY_ELEMENT:
1976     case EL_CURRENT_CE_VALUE:
1977     case EL_CURRENT_CE_SCORE:
1978     case EL_PREV_CE_1:
1979     case EL_PREV_CE_2:
1980     case EL_PREV_CE_3:
1981     case EL_PREV_CE_4:
1982     case EL_PREV_CE_5:
1983     case EL_PREV_CE_6:
1984     case EL_PREV_CE_7:
1985     case EL_PREV_CE_8:
1986     case EL_NEXT_CE_1:
1987     case EL_NEXT_CE_2:
1988     case EL_NEXT_CE_3:
1989     case EL_NEXT_CE_4:
1990     case EL_NEXT_CE_5:
1991     case EL_NEXT_CE_6:
1992     case EL_NEXT_CE_7:
1993     case EL_NEXT_CE_8:
1994       /* reference elements should not be used on the playfield */
1995       Feld[x][y] = EL_EMPTY;
1996       break;
1997
1998     default:
1999       if (IS_CUSTOM_ELEMENT(element))
2000       {
2001         if (CAN_MOVE(element))
2002           InitMovDir(x, y);
2003
2004 #if USE_NEW_CUSTOM_VALUE
2005         if (!element_info[element].use_last_ce_value || init_game)
2006           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2007 #endif
2008       }
2009       else if (IS_GROUP_ELEMENT(element))
2010       {
2011         Feld[x][y] = GetElementFromGroupElement(element);
2012
2013         InitField(x, y, init_game);
2014       }
2015
2016       break;
2017   }
2018
2019   if (!init_game)
2020     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2021 }
2022
2023 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2024 {
2025   InitField(x, y, init_game);
2026
2027   /* not needed to call InitMovDir() -- already done by InitField()! */
2028   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2029       CAN_MOVE(Feld[x][y]))
2030     InitMovDir(x, y);
2031 }
2032
2033 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2034 {
2035   int old_element = Feld[x][y];
2036
2037   InitField(x, y, init_game);
2038
2039   /* not needed to call InitMovDir() -- already done by InitField()! */
2040   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2041       CAN_MOVE(old_element) &&
2042       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2043     InitMovDir(x, y);
2044
2045   /* this case is in fact a combination of not less than three bugs:
2046      first, it calls InitMovDir() for elements that can move, although this is
2047      already done by InitField(); then, it checks the element that was at this
2048      field _before_ the call to InitField() (which can change it); lastly, it
2049      was not called for "mole with direction" elements, which were treated as
2050      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2051   */
2052 }
2053
2054 #if 1
2055
2056 static int get_key_element_from_nr(int key_nr)
2057 {
2058   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2059                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2060                           EL_EM_KEY_1 : EL_KEY_1);
2061
2062   return key_base_element + key_nr;
2063 }
2064
2065 static int get_next_dropped_element(struct PlayerInfo *player)
2066 {
2067   return (player->inventory_size > 0 ?
2068           player->inventory_element[player->inventory_size - 1] :
2069           player->inventory_infinite_element != EL_UNDEFINED ?
2070           player->inventory_infinite_element :
2071           player->dynabombs_left > 0 ?
2072           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2073           EL_UNDEFINED);
2074 }
2075
2076 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2077 {
2078   /* pos >= 0: get element from bottom of the stack;
2079      pos <  0: get element from top of the stack */
2080
2081   if (pos < 0)
2082   {
2083     int min_inventory_size = -pos;
2084     int inventory_pos = player->inventory_size - min_inventory_size;
2085     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2086
2087     return (player->inventory_size >= min_inventory_size ?
2088             player->inventory_element[inventory_pos] :
2089             player->inventory_infinite_element != EL_UNDEFINED ?
2090             player->inventory_infinite_element :
2091             player->dynabombs_left >= min_dynabombs_left ?
2092             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2093             EL_UNDEFINED);
2094   }
2095   else
2096   {
2097     int min_dynabombs_left = pos + 1;
2098     int min_inventory_size = pos + 1 - player->dynabombs_left;
2099     int inventory_pos = pos - player->dynabombs_left;
2100
2101     return (player->inventory_infinite_element != EL_UNDEFINED ?
2102             player->inventory_infinite_element :
2103             player->dynabombs_left >= min_dynabombs_left ?
2104             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2105             player->inventory_size >= min_inventory_size ?
2106             player->inventory_element[inventory_pos] :
2107             EL_UNDEFINED);
2108   }
2109 }
2110
2111 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2112 {
2113   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2114   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2115   int compare_result;
2116
2117   if (gpo1->sort_priority != gpo2->sort_priority)
2118     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2119   else
2120     compare_result = gpo1->nr - gpo2->nr;
2121
2122   return compare_result;
2123 }
2124
2125 void InitGameControlValues()
2126 {
2127   int i;
2128
2129   for (i = 0; game_panel_controls[i].nr != -1; i++)
2130   {
2131     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2132     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2133     struct TextPosInfo *pos = gpc->pos;
2134     int nr = gpc->nr;
2135     int type = gpc->type;
2136
2137     if (nr != i)
2138     {
2139       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2140       Error(ERR_EXIT, "this should not happen -- please debug");
2141     }
2142
2143     /* force update of game controls after initialization */
2144     gpc->value = gpc->last_value = -1;
2145     gpc->frame = gpc->last_frame = -1;
2146     gpc->gfx_frame = -1;
2147
2148     /* determine panel value width for later calculation of alignment */
2149     if (type == TYPE_INTEGER || type == TYPE_STRING)
2150     {
2151       pos->width = pos->size * getFontWidth(pos->font);
2152       pos->height = getFontHeight(pos->font);
2153     }
2154     else if (type == TYPE_ELEMENT)
2155     {
2156       pos->width = pos->size;
2157       pos->height = pos->size;
2158     }
2159
2160     /* fill structure for game panel draw order */
2161     gpo->nr = gpc->nr;
2162     gpo->sort_priority = pos->sort_priority;
2163   }
2164
2165   /* sort game panel controls according to sort_priority and control number */
2166   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2167         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2168 }
2169
2170 void UpdatePlayfieldElementCount()
2171 {
2172   boolean use_element_count = FALSE;
2173   int i, j, x, y;
2174
2175   /* first check if it is needed at all to calculate playfield element count */
2176   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2177     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2178       use_element_count = TRUE;
2179
2180   if (!use_element_count)
2181     return;
2182
2183   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2184     element_info[i].element_count = 0;
2185
2186   SCAN_PLAYFIELD(x, y)
2187   {
2188     element_info[Feld[x][y]].element_count++;
2189   }
2190
2191   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2192     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2193       if (IS_IN_GROUP(j, i))
2194         element_info[EL_GROUP_START + i].element_count +=
2195           element_info[j].element_count;
2196 }
2197
2198 void UpdateGameControlValues()
2199 {
2200   int i, k;
2201   int time = (local_player->LevelSolved ?
2202               local_player->LevelSolved_CountingTime :
2203               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204               level.native_em_level->lev->time :
2205               level.time == 0 ? TimePlayed : TimeLeft);
2206   int score = (local_player->LevelSolved ?
2207                local_player->LevelSolved_CountingScore :
2208                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2209                level.native_em_level->lev->score :
2210                local_player->score);
2211   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212               level.native_em_level->lev->required :
2213               local_player->gems_still_needed);
2214   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215                      level.native_em_level->lev->required > 0 :
2216                      local_player->gems_still_needed > 0 ||
2217                      local_player->sokobanfields_still_needed > 0 ||
2218                      local_player->lights_still_needed > 0);
2219
2220   UpdatePlayfieldElementCount();
2221
2222   /* update game panel control values */
2223
2224   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2225   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2226
2227   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2228   for (i = 0; i < MAX_NUM_KEYS; i++)
2229     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2230   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2231   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2232
2233   if (game.centered_player_nr == -1)
2234   {
2235     for (i = 0; i < MAX_PLAYERS; i++)
2236     {
2237       for (k = 0; k < MAX_NUM_KEYS; k++)
2238       {
2239         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2240         {
2241           if (level.native_em_level->ply[i]->keys & (1 << k))
2242             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243               get_key_element_from_nr(k);
2244         }
2245         else if (stored_player[i].key[k])
2246           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247             get_key_element_from_nr(k);
2248       }
2249
2250       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2251         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2252           level.native_em_level->ply[i]->dynamite;
2253       else
2254         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255           stored_player[i].inventory_size;
2256
2257       if (stored_player[i].num_white_keys > 0)
2258         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2259           EL_DC_KEY_WHITE;
2260
2261       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2262         stored_player[i].num_white_keys;
2263     }
2264   }
2265   else
2266   {
2267     int player_nr = game.centered_player_nr;
2268
2269     for (k = 0; k < MAX_NUM_KEYS; k++)
2270     {
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2272       {
2273         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2274           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275             get_key_element_from_nr(k);
2276       }
2277       else if (stored_player[player_nr].key[k])
2278         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279           get_key_element_from_nr(k);
2280     }
2281
2282     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2284         level.native_em_level->ply[player_nr]->dynamite;
2285     else
2286       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287         stored_player[player_nr].inventory_size;
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2314     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2315      EL_EMPTY);
2316   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2317     local_player->shield_normal_time_left;
2318   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2319     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2320      EL_EMPTY);
2321   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2322     local_player->shield_deadly_time_left;
2323
2324   game_panel_controls[GAME_PANEL_EXIT].value =
2325     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2326
2327   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2328     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2329   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2330     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2331      EL_EMC_MAGIC_BALL_SWITCH);
2332
2333   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2334     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2335   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2336     game.light_time_left;
2337
2338   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2339     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2340   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2341     game.timegate_time_left;
2342
2343   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2344     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2345
2346   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2347     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2348   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2349     game.lenses_time_left;
2350
2351   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2352     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2353   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2354     game.magnify_time_left;
2355
2356   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2357     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2358      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2359      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2360      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2361      EL_BALLOON_SWITCH_NONE);
2362
2363   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2364     local_player->dynabomb_count;
2365   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2366     local_player->dynabomb_size;
2367   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2368     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2369
2370   game_panel_controls[GAME_PANEL_PENGUINS].value =
2371     local_player->friends_still_needed;
2372
2373   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2374     local_player->sokobanfields_still_needed;
2375   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2376     local_player->sokobanfields_still_needed;
2377
2378   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2379     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2380
2381   for (i = 0; i < NUM_BELTS; i++)
2382   {
2383     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2384       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2385        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2386     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2387       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2388   }
2389
2390   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2391     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2392   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2393     game.magic_wall_time_left;
2394
2395 #if USE_PLAYER_GRAVITY
2396   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2397     local_player->gravity;
2398 #else
2399   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2400 #endif
2401
2402   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2403     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2404
2405   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2406     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2407       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2408        game.panel.element[i].id : EL_UNDEFINED);
2409
2410   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2412       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2413        element_info[game.panel.element_count[i].id].element_count : 0);
2414
2415   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2416     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2417       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2418        element_info[game.panel.ce_score[i].id].collect_score : 0);
2419
2420   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2422       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2423        element_info[game.panel.ce_score_element[i].id].collect_score :
2424        EL_UNDEFINED);
2425
2426   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2427   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2428   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2429
2430   /* update game panel control frames */
2431
2432   for (i = 0; game_panel_controls[i].nr != -1; i++)
2433   {
2434     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2435
2436     if (gpc->type == TYPE_ELEMENT)
2437     {
2438       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2439       {
2440         int last_anim_random_frame = gfx.anim_random_frame;
2441         int element = gpc->value;
2442         int graphic = el2panelimg(element);
2443
2444         if (gpc->value != gpc->last_value)
2445         {
2446           gpc->gfx_frame = 0;
2447           gpc->gfx_random = INIT_GFX_RANDOM();
2448         }
2449         else
2450         {
2451           gpc->gfx_frame++;
2452
2453           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2454               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2455             gpc->gfx_random = INIT_GFX_RANDOM();
2456         }
2457
2458         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2459           gfx.anim_random_frame = gpc->gfx_random;
2460
2461         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2462           gpc->gfx_frame = element_info[element].collect_score;
2463
2464         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2465                                               gpc->gfx_frame);
2466
2467         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2468           gfx.anim_random_frame = last_anim_random_frame;
2469       }
2470     }
2471   }
2472 }
2473
2474 void DisplayGameControlValues()
2475 {
2476   boolean redraw_panel = FALSE;
2477   int i;
2478
2479   for (i = 0; game_panel_controls[i].nr != -1; i++)
2480   {
2481     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2482
2483     if (PANEL_DEACTIVATED(gpc->pos))
2484       continue;
2485
2486     if (gpc->value == gpc->last_value &&
2487         gpc->frame == gpc->last_frame)
2488       continue;
2489
2490     redraw_panel = TRUE;
2491   }
2492
2493   if (!redraw_panel)
2494     return;
2495
2496   /* copy default game door content to main double buffer */
2497   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2498              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2499
2500   /* redraw game control buttons */
2501 #if 1
2502   RedrawGameButtons();
2503 #else
2504   UnmapGameButtons();
2505   MapGameButtons();
2506 #endif
2507
2508   game_status = GAME_MODE_PSEUDO_PANEL;
2509
2510 #if 1
2511   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2512 #else
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514 #endif
2515   {
2516 #if 1
2517     int nr = game_panel_order[i].nr;
2518     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2519 #else
2520     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2521     int nr = gpc->nr;
2522 #endif
2523     struct TextPosInfo *pos = gpc->pos;
2524     int type = gpc->type;
2525     int value = gpc->value;
2526     int frame = gpc->frame;
2527 #if 0
2528     int last_value = gpc->last_value;
2529     int last_frame = gpc->last_frame;
2530 #endif
2531     int size = pos->size;
2532     int font = pos->font;
2533     boolean draw_masked = pos->draw_masked;
2534     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2535
2536     if (PANEL_DEACTIVATED(pos))
2537       continue;
2538
2539 #if 0
2540     if (value == last_value && frame == last_frame)
2541       continue;
2542 #endif
2543
2544     gpc->last_value = value;
2545     gpc->last_frame = frame;
2546
2547 #if 0
2548     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2549 #endif
2550
2551     if (type == TYPE_INTEGER)
2552     {
2553       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2554           nr == GAME_PANEL_TIME)
2555       {
2556         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2557
2558         if (use_dynamic_size)           /* use dynamic number of digits */
2559         {
2560           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2561           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2562           int size2 = size1 + 1;
2563           int font1 = pos->font;
2564           int font2 = pos->font_alt;
2565
2566           size = (value < value_change ? size1 : size2);
2567           font = (value < value_change ? font1 : font2);
2568
2569 #if 0
2570           /* clear background if value just changed its size (dynamic digits) */
2571           if ((last_value < value_change) != (value < value_change))
2572           {
2573             int width1 = size1 * getFontWidth(font1);
2574             int width2 = size2 * getFontWidth(font2);
2575             int max_width = MAX(width1, width2);
2576             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2577
2578             pos->width = max_width;
2579
2580             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2581                                        max_width, max_height);
2582           }
2583 #endif
2584         }
2585       }
2586
2587 #if 1
2588       /* correct text size if "digits" is zero or less */
2589       if (size <= 0)
2590         size = strlen(int2str(value, size));
2591
2592       /* dynamically correct text alignment */
2593       pos->width = size * getFontWidth(font);
2594 #endif
2595
2596       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2597                   int2str(value, size), font, mask_mode);
2598     }
2599     else if (type == TYPE_ELEMENT)
2600     {
2601       int element, graphic;
2602       Bitmap *src_bitmap;
2603       int src_x, src_y;
2604       int width, height;
2605       int dst_x = PANEL_XPOS(pos);
2606       int dst_y = PANEL_YPOS(pos);
2607
2608 #if 1
2609       if (value != EL_UNDEFINED && value != EL_EMPTY)
2610       {
2611         element = value;
2612         graphic = el2panelimg(value);
2613
2614         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2615
2616 #if 1
2617         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2618           size = TILESIZE;
2619 #endif
2620
2621         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2622                               &src_x, &src_y);
2623
2624         width  = graphic_info[graphic].width  * size / TILESIZE;
2625         height = graphic_info[graphic].height * size / TILESIZE;
2626
2627         if (draw_masked)
2628         {
2629           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2630                         dst_x - src_x, dst_y - src_y);
2631           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2632                            dst_x, dst_y);
2633         }
2634         else
2635         {
2636           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2637                      dst_x, dst_y);
2638         }
2639       }
2640 #else
2641       if (value == EL_UNDEFINED || value == EL_EMPTY)
2642       {
2643         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2644         graphic = el2panelimg(element);
2645
2646         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2647         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2648         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2649       }
2650       else
2651       {
2652         element = value;
2653         graphic = el2panelimg(value);
2654
2655         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2656       }
2657
2658       width  = graphic_info[graphic].width  * size / TILESIZE;
2659       height = graphic_info[graphic].height * size / TILESIZE;
2660
2661       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2662 #endif
2663     }
2664     else if (type == TYPE_STRING)
2665     {
2666       boolean active = (value != 0);
2667       char *state_normal = "off";
2668       char *state_active = "on";
2669       char *state = (active ? state_active : state_normal);
2670       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2671                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2672                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2673                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2674
2675       if (nr == GAME_PANEL_GRAVITY_STATE)
2676       {
2677         int font1 = pos->font;          /* (used for normal state) */
2678         int font2 = pos->font_alt;      /* (used for active state) */
2679 #if 0
2680         int size1 = strlen(state_normal);
2681         int size2 = strlen(state_active);
2682         int width1 = size1 * getFontWidth(font1);
2683         int width2 = size2 * getFontWidth(font2);
2684         int max_width = MAX(width1, width2);
2685         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2686
2687         pos->width = max_width;
2688
2689         /* clear background for values that may have changed its size */
2690         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2691                                    max_width, max_height);
2692 #endif
2693
2694         font = (active ? font2 : font1);
2695       }
2696
2697       if (s != NULL)
2698       {
2699         char *s_cut;
2700
2701 #if 1
2702         if (size <= 0)
2703         {
2704           /* don't truncate output if "chars" is zero or less */
2705           size = strlen(s);
2706
2707           /* dynamically correct text alignment */
2708           pos->width = size * getFontWidth(font);
2709         }
2710 #endif
2711
2712         s_cut = getStringCopyN(s, size);
2713
2714         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2715                     s_cut, font, mask_mode);
2716
2717         free(s_cut);
2718       }
2719     }
2720
2721     redraw_mask |= REDRAW_DOOR_1;
2722   }
2723
2724   game_status = GAME_MODE_PLAYING;
2725 }
2726
2727 void UpdateAndDisplayGameControlValues()
2728 {
2729   if (tape.warp_forward)
2730     return;
2731
2732   UpdateGameControlValues();
2733   DisplayGameControlValues();
2734 }
2735
2736 void DrawGameValue_Emeralds(int value)
2737 {
2738   struct TextPosInfo *pos = &game.panel.gems;
2739 #if 1
2740   int font_nr = pos->font;
2741 #else
2742   int font_nr = FONT_TEXT_2;
2743 #endif
2744   int font_width = getFontWidth(font_nr);
2745   int chars = pos->size;
2746
2747 #if 1
2748   return;       /* !!! USE NEW STUFF !!! */
2749 #endif
2750
2751   if (PANEL_DEACTIVATED(pos))
2752     return;
2753
2754   pos->width = chars * font_width;
2755
2756   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2757 }
2758
2759 void DrawGameValue_Dynamite(int value)
2760 {
2761   struct TextPosInfo *pos = &game.panel.inventory_count;
2762 #if 1
2763   int font_nr = pos->font;
2764 #else
2765   int font_nr = FONT_TEXT_2;
2766 #endif
2767   int font_width = getFontWidth(font_nr);
2768   int chars = pos->size;
2769
2770 #if 1
2771   return;       /* !!! USE NEW STUFF !!! */
2772 #endif
2773
2774   if (PANEL_DEACTIVATED(pos))
2775     return;
2776
2777   pos->width = chars * font_width;
2778
2779   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2780 }
2781
2782 void DrawGameValue_Score(int value)
2783 {
2784   struct TextPosInfo *pos = &game.panel.score;
2785 #if 1
2786   int font_nr = pos->font;
2787 #else
2788   int font_nr = FONT_TEXT_2;
2789 #endif
2790   int font_width = getFontWidth(font_nr);
2791   int chars = pos->size;
2792
2793 #if 1
2794   return;       /* !!! USE NEW STUFF !!! */
2795 #endif
2796
2797   if (PANEL_DEACTIVATED(pos))
2798     return;
2799
2800   pos->width = chars * font_width;
2801
2802   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2803 }
2804
2805 void DrawGameValue_Time(int value)
2806 {
2807   struct TextPosInfo *pos = &game.panel.time;
2808   static int last_value = -1;
2809   int chars1 = 3;
2810   int chars2 = 4;
2811   int chars = pos->size;
2812 #if 1
2813   int font1_nr = pos->font;
2814   int font2_nr = pos->font_alt;
2815 #else
2816   int font1_nr = FONT_TEXT_2;
2817   int font2_nr = FONT_TEXT_1;
2818 #endif
2819   int font_nr = font1_nr;
2820   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   if (use_dynamic_chars)                /* use dynamic number of chars */
2830   {
2831     chars   = (value < 1000 ? chars1   : chars2);
2832     font_nr = (value < 1000 ? font1_nr : font2_nr);
2833   }
2834
2835   /* clear background if value just changed its size (dynamic chars only) */
2836   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2837   {
2838     int width1 = chars1 * getFontWidth(font1_nr);
2839     int width2 = chars2 * getFontWidth(font2_nr);
2840     int max_width = MAX(width1, width2);
2841     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2842
2843     pos->width = max_width;
2844
2845     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2846                                max_width, max_height);
2847   }
2848
2849   pos->width = chars * getFontWidth(font_nr);
2850
2851   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2852
2853   last_value = value;
2854 }
2855
2856 void DrawGameValue_Level(int value)
2857 {
2858   struct TextPosInfo *pos = &game.panel.level_number;
2859   int chars1 = 2;
2860   int chars2 = 3;
2861   int chars = pos->size;
2862 #if 1
2863   int font1_nr = pos->font;
2864   int font2_nr = pos->font_alt;
2865 #else
2866   int font1_nr = FONT_TEXT_2;
2867   int font2_nr = FONT_TEXT_1;
2868 #endif
2869   int font_nr = font1_nr;
2870   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2871
2872 #if 1
2873   return;       /* !!! USE NEW STUFF !!! */
2874 #endif
2875
2876   if (PANEL_DEACTIVATED(pos))
2877     return;
2878
2879   if (use_dynamic_chars)                /* use dynamic number of chars */
2880   {
2881     chars   = (level_nr < 100 ? chars1   : chars2);
2882     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2883   }
2884
2885   pos->width = chars * getFontWidth(font_nr);
2886
2887   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2888 }
2889
2890 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2891 {
2892 #if 0
2893   struct TextPosInfo *pos = &game.panel.keys;
2894 #endif
2895 #if 0
2896   int base_key_graphic = EL_KEY_1;
2897 #endif
2898   int i;
2899
2900 #if 1
2901   return;       /* !!! USE NEW STUFF !!! */
2902 #endif
2903
2904 #if 0
2905   if (PANEL_DEACTIVATED(pos))
2906     return;
2907 #endif
2908
2909 #if 0
2910   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2911     base_key_graphic = EL_EM_KEY_1;
2912 #endif
2913
2914 #if 0
2915   pos->width = 4 * MINI_TILEX;
2916 #endif
2917
2918 #if 1
2919   for (i = 0; i < MAX_NUM_KEYS; i++)
2920 #else
2921   /* currently only 4 of 8 possible keys are displayed */
2922   for (i = 0; i < STD_NUM_KEYS; i++)
2923 #endif
2924   {
2925 #if 1
2926     struct TextPosInfo *pos = &game.panel.key[i];
2927 #endif
2928     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2929     int src_y = DOOR_GFX_PAGEY1 + 123;
2930 #if 1
2931     int dst_x = PANEL_XPOS(pos);
2932     int dst_y = PANEL_YPOS(pos);
2933 #else
2934     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2935     int dst_y = PANEL_YPOS(pos);
2936 #endif
2937
2938 #if 1
2939     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2940                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2941                    EL_KEY_1) + i;
2942     int graphic = el2edimg(element);
2943 #endif
2944
2945 #if 1
2946     if (PANEL_DEACTIVATED(pos))
2947       continue;
2948 #endif
2949
2950 #if 0
2951     /* masked blit with tiles from half-size scaled bitmap does not work yet
2952        (no mask bitmap created for these sizes after loading and scaling) --
2953        solution: load without creating mask, scale, then create final mask */
2954
2955     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2956                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2957
2958     if (key[i])
2959     {
2960 #if 0
2961       int graphic = el2edimg(base_key_graphic + i);
2962 #endif
2963       Bitmap *src_bitmap;
2964       int src_x, src_y;
2965
2966       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2967
2968       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2969                     dst_x - src_x, dst_y - src_y);
2970       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2971                        dst_x, dst_y);
2972     }
2973 #else
2974 #if 1
2975     if (key[i])
2976       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2977     else
2978       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2979                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2980 #else
2981     if (key[i])
2982       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2983     else
2984       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2985                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2986 #endif
2987 #endif
2988   }
2989 }
2990
2991 #else
2992
2993 void DrawGameValue_Emeralds(int value)
2994 {
2995   int font_nr = FONT_TEXT_2;
2996   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2997
2998   if (PANEL_DEACTIVATED(game.panel.gems))
2999     return;
3000
3001   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3002 }
3003
3004 void DrawGameValue_Dynamite(int value)
3005 {
3006   int font_nr = FONT_TEXT_2;
3007   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3008
3009   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3010     return;
3011
3012   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3013 }
3014
3015 void DrawGameValue_Score(int value)
3016 {
3017   int font_nr = FONT_TEXT_2;
3018   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3019
3020   if (PANEL_DEACTIVATED(game.panel.score))
3021     return;
3022
3023   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3024 }
3025
3026 void DrawGameValue_Time(int value)
3027 {
3028   int font1_nr = FONT_TEXT_2;
3029 #if 1
3030   int font2_nr = FONT_TEXT_1;
3031 #else
3032   int font2_nr = FONT_LEVEL_NUMBER;
3033 #endif
3034   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3035   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3036
3037   if (PANEL_DEACTIVATED(game.panel.time))
3038     return;
3039
3040   /* clear background if value just changed its size */
3041   if (value == 999 || value == 1000)
3042     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3043
3044   if (value < 1000)
3045     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3046   else
3047     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3048 }
3049
3050 void DrawGameValue_Level(int value)
3051 {
3052   int font1_nr = FONT_TEXT_2;
3053 #if 1
3054   int font2_nr = FONT_TEXT_1;
3055 #else
3056   int font2_nr = FONT_LEVEL_NUMBER;
3057 #endif
3058
3059   if (PANEL_DEACTIVATED(game.panel.level))
3060     return;
3061
3062   if (level_nr < 100)
3063     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3064   else
3065     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3066 }
3067
3068 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3069 {
3070   int base_key_graphic = EL_KEY_1;
3071   int i;
3072
3073   if (PANEL_DEACTIVATED(game.panel.keys))
3074     return;
3075
3076   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3077     base_key_graphic = EL_EM_KEY_1;
3078
3079   /* currently only 4 of 8 possible keys are displayed */
3080   for (i = 0; i < STD_NUM_KEYS; i++)
3081   {
3082     int x = XX_KEYS + i * MINI_TILEX;
3083     int y = YY_KEYS;
3084
3085     if (key[i])
3086       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3087     else
3088       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3089                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3090   }
3091 }
3092
3093 #endif
3094
3095 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3096                        int key_bits)
3097 {
3098   int key[MAX_NUM_KEYS];
3099   int i;
3100
3101   /* prevent EM engine from updating time/score values parallel to GameWon() */
3102   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3103       local_player->LevelSolved)
3104     return;
3105
3106   for (i = 0; i < MAX_NUM_KEYS; i++)
3107     key[i] = key_bits & (1 << i);
3108
3109   DrawGameValue_Level(level_nr);
3110
3111   DrawGameValue_Emeralds(emeralds);
3112   DrawGameValue_Dynamite(dynamite);
3113   DrawGameValue_Score(score);
3114   DrawGameValue_Time(time);
3115
3116   DrawGameValue_Keys(key);
3117 }
3118
3119 void UpdateGameDoorValues()
3120 {
3121   UpdateGameControlValues();
3122 }
3123
3124 void DrawGameDoorValues()
3125 {
3126   DisplayGameControlValues();
3127 }
3128
3129 void DrawGameDoorValues_OLD()
3130 {
3131   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3132   int dynamite_value = 0;
3133   int score_value = (local_player->LevelSolved ? local_player->score_final :
3134                      local_player->score);
3135   int gems_value = local_player->gems_still_needed;
3136   int key_bits = 0;
3137   int i, j;
3138
3139   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3140   {
3141     DrawGameDoorValues_EM();
3142
3143     return;
3144   }
3145
3146   if (game.centered_player_nr == -1)
3147   {
3148     for (i = 0; i < MAX_PLAYERS; i++)
3149     {
3150       for (j = 0; j < MAX_NUM_KEYS; j++)
3151         if (stored_player[i].key[j])
3152           key_bits |= (1 << j);
3153
3154       dynamite_value += stored_player[i].inventory_size;
3155     }
3156   }
3157   else
3158   {
3159     int player_nr = game.centered_player_nr;
3160
3161     for (i = 0; i < MAX_NUM_KEYS; i++)
3162       if (stored_player[player_nr].key[i])
3163         key_bits |= (1 << i);
3164
3165     dynamite_value = stored_player[player_nr].inventory_size;
3166   }
3167
3168   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3169                     key_bits);
3170 }
3171
3172
3173 /*
3174   =============================================================================
3175   InitGameEngine()
3176   -----------------------------------------------------------------------------
3177   initialize game engine due to level / tape version number
3178   =============================================================================
3179 */
3180
3181 static void InitGameEngine()
3182 {
3183   int i, j, k, l, x, y;
3184
3185   /* set game engine from tape file when re-playing, else from level file */
3186   game.engine_version = (tape.playing ? tape.engine_version :
3187                          level.game_version);
3188
3189   /* ---------------------------------------------------------------------- */
3190   /* set flags for bugs and changes according to active game engine version */
3191   /* ---------------------------------------------------------------------- */
3192
3193   /*
3194     Summary of bugfix/change:
3195     Fixed handling for custom elements that change when pushed by the player.
3196
3197     Fixed/changed in version:
3198     3.1.0
3199
3200     Description:
3201     Before 3.1.0, custom elements that "change when pushing" changed directly
3202     after the player started pushing them (until then handled in "DigField()").
3203     Since 3.1.0, these custom elements are not changed until the "pushing"
3204     move of the element is finished (now handled in "ContinueMoving()").
3205
3206     Affected levels/tapes:
3207     The first condition is generally needed for all levels/tapes before version
3208     3.1.0, which might use the old behaviour before it was changed; known tapes
3209     that are affected are some tapes from the level set "Walpurgis Gardens" by
3210     Jamie Cullen.
3211     The second condition is an exception from the above case and is needed for
3212     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3213     above (including some development versions of 3.1.0), but before it was
3214     known that this change would break tapes like the above and was fixed in
3215     3.1.1, so that the changed behaviour was active although the engine version
3216     while recording maybe was before 3.1.0. There is at least one tape that is
3217     affected by this exception, which is the tape for the one-level set "Bug
3218     Machine" by Juergen Bonhagen.
3219   */
3220
3221   game.use_change_when_pushing_bug =
3222     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3223      !(tape.playing &&
3224        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3225        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3226
3227   /*
3228     Summary of bugfix/change:
3229     Fixed handling for blocking the field the player leaves when moving.
3230
3231     Fixed/changed in version:
3232     3.1.1
3233
3234     Description:
3235     Before 3.1.1, when "block last field when moving" was enabled, the field
3236     the player is leaving when moving was blocked for the time of the move,
3237     and was directly unblocked afterwards. This resulted in the last field
3238     being blocked for exactly one less than the number of frames of one player
3239     move. Additionally, even when blocking was disabled, the last field was
3240     blocked for exactly one frame.
3241     Since 3.1.1, due to changes in player movement handling, the last field
3242     is not blocked at all when blocking is disabled. When blocking is enabled,
3243     the last field is blocked for exactly the number of frames of one player
3244     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3245     last field is blocked for exactly one more than the number of frames of
3246     one player move.
3247
3248     Affected levels/tapes:
3249     (!!! yet to be determined -- probably many !!!)
3250   */
3251
3252   game.use_block_last_field_bug =
3253     (game.engine_version < VERSION_IDENT(3,1,1,0));
3254
3255   /*
3256     Summary of bugfix/change:
3257     Changed behaviour of CE changes with multiple changes per single frame.
3258
3259     Fixed/changed in version:
3260     3.2.0-6
3261
3262     Description:
3263     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3264     This resulted in race conditions where CEs seem to behave strange in some
3265     situations (where triggered CE changes were just skipped because there was
3266     already a CE change on that tile in the playfield in that engine frame).
3267     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3268     (The number of changes per frame must be limited in any case, because else
3269     it is easily possible to define CE changes that would result in an infinite
3270     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3271     should be set large enough so that it would only be reached in cases where
3272     the corresponding CE change conditions run into a loop. Therefore, it seems
3273     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3274     maximal number of change pages for custom elements.)
3275
3276     Affected levels/tapes:
3277     Probably many.
3278   */
3279
3280 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3281   game.max_num_changes_per_frame = 1;
3282 #else
3283   game.max_num_changes_per_frame =
3284     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3285 #endif
3286
3287   /* ---------------------------------------------------------------------- */
3288
3289   /* default scan direction: scan playfield from top/left to bottom/right */
3290   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3291
3292   /* dynamically adjust element properties according to game engine version */
3293   InitElementPropertiesEngine(game.engine_version);
3294
3295 #if 0
3296   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3297   printf("          tape version == %06d [%s] [file: %06d]\n",
3298          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3299          tape.file_version);
3300   printf("       => game.engine_version == %06d\n", game.engine_version);
3301 #endif
3302
3303   /* ---------- initialize player's initial move delay --------------------- */
3304
3305   /* dynamically adjust player properties according to level information */
3306   for (i = 0; i < MAX_PLAYERS; i++)
3307     game.initial_move_delay_value[i] =
3308       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3309
3310   /* dynamically adjust player properties according to game engine version */
3311   for (i = 0; i < MAX_PLAYERS; i++)
3312     game.initial_move_delay[i] =
3313       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3314        game.initial_move_delay_value[i] : 0);
3315
3316   /* ---------- initialize player's initial push delay --------------------- */
3317
3318   /* dynamically adjust player properties according to game engine version */
3319   game.initial_push_delay_value =
3320     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3321
3322   /* ---------- initialize changing elements ------------------------------- */
3323
3324   /* initialize changing elements information */
3325   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3326   {
3327     struct ElementInfo *ei = &element_info[i];
3328
3329     /* this pointer might have been changed in the level editor */
3330     ei->change = &ei->change_page[0];
3331
3332     if (!IS_CUSTOM_ELEMENT(i))
3333     {
3334       ei->change->target_element = EL_EMPTY_SPACE;
3335       ei->change->delay_fixed = 0;
3336       ei->change->delay_random = 0;
3337       ei->change->delay_frames = 1;
3338     }
3339
3340     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3341     {
3342       ei->has_change_event[j] = FALSE;
3343
3344       ei->event_page_nr[j] = 0;
3345       ei->event_page[j] = &ei->change_page[0];
3346     }
3347   }
3348
3349   /* add changing elements from pre-defined list */
3350   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3351   {
3352     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3353     struct ElementInfo *ei = &element_info[ch_delay->element];
3354
3355     ei->change->target_element       = ch_delay->target_element;
3356     ei->change->delay_fixed          = ch_delay->change_delay;
3357
3358     ei->change->pre_change_function  = ch_delay->pre_change_function;
3359     ei->change->change_function      = ch_delay->change_function;
3360     ei->change->post_change_function = ch_delay->post_change_function;
3361
3362     ei->change->can_change = TRUE;
3363     ei->change->can_change_or_has_action = TRUE;
3364
3365     ei->has_change_event[CE_DELAY] = TRUE;
3366
3367     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3368     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3369   }
3370
3371   /* ---------- initialize internal run-time variables --------------------- */
3372
3373   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3374   {
3375     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3376
3377     for (j = 0; j < ei->num_change_pages; j++)
3378     {
3379       ei->change_page[j].can_change_or_has_action =
3380         (ei->change_page[j].can_change |
3381          ei->change_page[j].has_action);
3382     }
3383   }
3384
3385   /* add change events from custom element configuration */
3386   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3387   {
3388     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3389
3390     for (j = 0; j < ei->num_change_pages; j++)
3391     {
3392       if (!ei->change_page[j].can_change_or_has_action)
3393         continue;
3394
3395       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3396       {
3397         /* only add event page for the first page found with this event */
3398         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3399         {
3400           ei->has_change_event[k] = TRUE;
3401
3402           ei->event_page_nr[k] = j;
3403           ei->event_page[k] = &ei->change_page[j];
3404         }
3405       }
3406     }
3407   }
3408
3409 #if 1
3410   /* ---------- initialize reference elements in change conditions --------- */
3411
3412   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3413   {
3414     int element = EL_CUSTOM_START + i;
3415     struct ElementInfo *ei = &element_info[element];
3416
3417     for (j = 0; j < ei->num_change_pages; j++)
3418     {
3419       int trigger_element = ei->change_page[j].initial_trigger_element;
3420
3421       if (trigger_element >= EL_PREV_CE_8 &&
3422           trigger_element <= EL_NEXT_CE_8)
3423         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3424
3425       ei->change_page[j].trigger_element = trigger_element;
3426     }
3427   }
3428 #endif
3429
3430   /* ---------- initialize run-time trigger player and element ------------- */
3431
3432   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3433   {
3434     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3439       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3440       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3441       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3442       ei->change_page[j].actual_trigger_ce_value = 0;
3443       ei->change_page[j].actual_trigger_ce_score = 0;
3444     }
3445   }
3446
3447   /* ---------- initialize trigger events ---------------------------------- */
3448
3449   /* initialize trigger events information */
3450   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3451     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3452       trigger_events[i][j] = FALSE;
3453
3454   /* add trigger events from element change event properties */
3455   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456   {
3457     struct ElementInfo *ei = &element_info[i];
3458
3459     for (j = 0; j < ei->num_change_pages; j++)
3460     {
3461       if (!ei->change_page[j].can_change_or_has_action)
3462         continue;
3463
3464       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3465       {
3466         int trigger_element = ei->change_page[j].trigger_element;
3467
3468         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3469         {
3470           if (ei->change_page[j].has_event[k])
3471           {
3472             if (IS_GROUP_ELEMENT(trigger_element))
3473             {
3474               struct ElementGroupInfo *group =
3475                 element_info[trigger_element].group;
3476
3477               for (l = 0; l < group->num_elements_resolved; l++)
3478                 trigger_events[group->element_resolved[l]][k] = TRUE;
3479             }
3480             else if (trigger_element == EL_ANY_ELEMENT)
3481               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3482                 trigger_events[l][k] = TRUE;
3483             else
3484               trigger_events[trigger_element][k] = TRUE;
3485           }
3486         }
3487       }
3488     }
3489   }
3490
3491   /* ---------- initialize push delay -------------------------------------- */
3492
3493   /* initialize push delay values to default */
3494   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3495   {
3496     if (!IS_CUSTOM_ELEMENT(i))
3497     {
3498       /* set default push delay values (corrected since version 3.0.7-1) */
3499       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3500       {
3501         element_info[i].push_delay_fixed = 2;
3502         element_info[i].push_delay_random = 8;
3503       }
3504       else
3505       {
3506         element_info[i].push_delay_fixed = 8;
3507         element_info[i].push_delay_random = 8;
3508       }
3509     }
3510   }
3511
3512   /* set push delay value for certain elements from pre-defined list */
3513   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3514   {
3515     int e = push_delay_list[i].element;
3516
3517     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3518     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3519   }
3520
3521   /* set push delay value for Supaplex elements for newer engine versions */
3522   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3523   {
3524     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3525     {
3526       if (IS_SP_ELEMENT(i))
3527       {
3528         /* set SP push delay to just enough to push under a falling zonk */
3529         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3530
3531         element_info[i].push_delay_fixed  = delay;
3532         element_info[i].push_delay_random = 0;
3533       }
3534     }
3535   }
3536
3537   /* ---------- initialize move stepsize ----------------------------------- */
3538
3539   /* initialize move stepsize values to default */
3540   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3541     if (!IS_CUSTOM_ELEMENT(i))
3542       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3543
3544   /* set move stepsize value for certain elements from pre-defined list */
3545   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3546   {
3547     int e = move_stepsize_list[i].element;
3548
3549     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3550   }
3551
3552   /* ---------- initialize collect score ----------------------------------- */
3553
3554   /* initialize collect score values for custom elements from initial value */
3555   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3556     if (IS_CUSTOM_ELEMENT(i))
3557       element_info[i].collect_score = element_info[i].collect_score_initial;
3558
3559   /* ---------- initialize collect count ----------------------------------- */
3560
3561   /* initialize collect count values for non-custom elements */
3562   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3563     if (!IS_CUSTOM_ELEMENT(i))
3564       element_info[i].collect_count_initial = 0;
3565
3566   /* add collect count values for all elements from pre-defined list */
3567   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3568     element_info[collect_count_list[i].element].collect_count_initial =
3569       collect_count_list[i].count;
3570
3571   /* ---------- initialize access direction -------------------------------- */
3572
3573   /* initialize access direction values to default (access from every side) */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (!IS_CUSTOM_ELEMENT(i))
3576       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3577
3578   /* set access direction value for certain elements from pre-defined list */
3579   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3580     element_info[access_direction_list[i].element].access_direction =
3581       access_direction_list[i].direction;
3582
3583   /* ---------- initialize explosion content ------------------------------- */
3584   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3585   {
3586     if (IS_CUSTOM_ELEMENT(i))
3587       continue;
3588
3589     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3590     {
3591       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3592
3593       element_info[i].content.e[x][y] =
3594         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3595          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3596          i == EL_PLAYER_3 ? EL_EMERALD :
3597          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3598          i == EL_MOLE ? EL_EMERALD_RED :
3599          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3600          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3601          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3602          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3603          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3604          i == EL_WALL_EMERALD ? EL_EMERALD :
3605          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3606          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3607          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3608          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3609          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3610          i == EL_WALL_PEARL ? EL_PEARL :
3611          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3612          EL_EMPTY);
3613     }
3614   }
3615
3616   /* ---------- initialize recursion detection ------------------------------ */
3617   recursion_loop_depth = 0;
3618   recursion_loop_detected = FALSE;
3619   recursion_loop_element = EL_UNDEFINED;
3620
3621   /* ---------- initialize graphics engine ---------------------------------- */
3622   game.scroll_delay_value =
3623     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3624      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3625   game.scroll_delay_value =
3626     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3627 }
3628
3629 int get_num_special_action(int element, int action_first, int action_last)
3630 {
3631   int num_special_action = 0;
3632   int i, j;
3633
3634   for (i = action_first; i <= action_last; i++)
3635   {
3636     boolean found = FALSE;
3637
3638     for (j = 0; j < NUM_DIRECTIONS; j++)
3639       if (el_act_dir2img(element, i, j) !=
3640           el_act_dir2img(element, ACTION_DEFAULT, j))
3641         found = TRUE;
3642
3643     if (found)
3644       num_special_action++;
3645     else
3646       break;
3647   }
3648
3649   return num_special_action;
3650 }
3651
3652
3653 /*
3654   =============================================================================
3655   InitGame()
3656   -----------------------------------------------------------------------------
3657   initialize and start new game
3658   =============================================================================
3659 */
3660
3661 void InitGame()
3662 {
3663   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3664   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3665   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3666 #if 0
3667   boolean do_fading = (game_status == GAME_MODE_MAIN);
3668 #endif
3669   int i, j, x, y;
3670
3671   game_status = GAME_MODE_PLAYING;
3672
3673   InitGameEngine();
3674   InitGameControlValues();
3675
3676   /* don't play tapes over network */
3677   network_playing = (options.network && !tape.playing);
3678
3679   for (i = 0; i < MAX_PLAYERS; i++)
3680   {
3681     struct PlayerInfo *player = &stored_player[i];
3682
3683     player->index_nr = i;
3684     player->index_bit = (1 << i);
3685     player->element_nr = EL_PLAYER_1 + i;
3686
3687     player->present = FALSE;
3688     player->active = FALSE;
3689     player->killed = FALSE;
3690
3691     player->action = 0;
3692     player->effective_action = 0;
3693     player->programmed_action = 0;
3694
3695     player->score = 0;
3696     player->score_final = 0;
3697
3698     player->gems_still_needed = level.gems_needed;
3699     player->sokobanfields_still_needed = 0;
3700     player->lights_still_needed = 0;
3701     player->friends_still_needed = 0;
3702
3703     for (j = 0; j < MAX_NUM_KEYS; j++)
3704       player->key[j] = FALSE;
3705
3706     player->num_white_keys = 0;
3707
3708     player->dynabomb_count = 0;
3709     player->dynabomb_size = 1;
3710     player->dynabombs_left = 0;
3711     player->dynabomb_xl = FALSE;
3712
3713     player->MovDir = MV_NONE;
3714     player->MovPos = 0;
3715     player->GfxPos = 0;
3716     player->GfxDir = MV_NONE;
3717     player->GfxAction = ACTION_DEFAULT;
3718     player->Frame = 0;
3719     player->StepFrame = 0;
3720
3721     player->initial_element = player->element_nr;
3722     player->artwork_element =
3723       (level.use_artwork_element[i] ? level.artwork_element[i] :
3724        player->element_nr);
3725     player->use_murphy = FALSE;
3726
3727     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3728     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3729
3730     player->gravity = level.initial_player_gravity[i];
3731
3732     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3733
3734     player->actual_frame_counter = 0;
3735
3736     player->step_counter = 0;
3737
3738     player->last_move_dir = MV_NONE;
3739
3740     player->is_active = FALSE;
3741
3742     player->is_waiting = FALSE;
3743     player->is_moving = FALSE;
3744     player->is_auto_moving = FALSE;
3745     player->is_digging = FALSE;
3746     player->is_snapping = FALSE;
3747     player->is_collecting = FALSE;
3748     player->is_pushing = FALSE;
3749     player->is_switching = FALSE;
3750     player->is_dropping = FALSE;
3751     player->is_dropping_pressed = FALSE;
3752
3753     player->is_bored = FALSE;
3754     player->is_sleeping = FALSE;
3755
3756     player->frame_counter_bored = -1;
3757     player->frame_counter_sleeping = -1;
3758
3759     player->anim_delay_counter = 0;
3760     player->post_delay_counter = 0;
3761
3762     player->dir_waiting = MV_NONE;
3763     player->action_waiting = ACTION_DEFAULT;
3764     player->last_action_waiting = ACTION_DEFAULT;
3765     player->special_action_bored = ACTION_DEFAULT;
3766     player->special_action_sleeping = ACTION_DEFAULT;
3767
3768     player->switch_x = -1;
3769     player->switch_y = -1;
3770
3771     player->drop_x = -1;
3772     player->drop_y = -1;
3773
3774     player->show_envelope = 0;
3775
3776     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3777
3778     player->push_delay       = -1;      /* initialized when pushing starts */
3779     player->push_delay_value = game.initial_push_delay_value;
3780
3781     player->drop_delay = 0;
3782     player->drop_pressed_delay = 0;
3783
3784     player->last_jx = -1;
3785     player->last_jy = -1;
3786     player->jx = -1;
3787     player->jy = -1;
3788
3789     player->shield_normal_time_left = 0;
3790     player->shield_deadly_time_left = 0;
3791
3792     player->inventory_infinite_element = EL_UNDEFINED;
3793     player->inventory_size = 0;
3794
3795     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3796     SnapField(player, 0, 0);
3797
3798     player->LevelSolved = FALSE;
3799     player->GameOver = FALSE;
3800
3801     player->LevelSolved_GameWon = FALSE;
3802     player->LevelSolved_GameEnd = FALSE;
3803     player->LevelSolved_PanelOff = FALSE;
3804     player->LevelSolved_SaveTape = FALSE;
3805     player->LevelSolved_SaveScore = FALSE;
3806     player->LevelSolved_CountingTime = 0;
3807     player->LevelSolved_CountingScore = 0;
3808   }
3809
3810   network_player_action_received = FALSE;
3811
3812 #if defined(NETWORK_AVALIABLE)
3813   /* initial null action */
3814   if (network_playing)
3815     SendToServer_MovePlayer(MV_NONE);
3816 #endif
3817
3818   ZX = ZY = -1;
3819   ExitX = ExitY = -1;
3820
3821   FrameCounter = 0;
3822   TimeFrames = 0;
3823   TimePlayed = 0;
3824   TimeLeft = level.time;
3825   TapeTime = 0;
3826
3827   ScreenMovDir = MV_NONE;
3828   ScreenMovPos = 0;
3829   ScreenGfxPos = 0;
3830
3831   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3832
3833   AllPlayersGone = FALSE;
3834
3835   game.yamyam_content_nr = 0;
3836   game.robot_wheel_active = FALSE;
3837   game.magic_wall_active = FALSE;
3838   game.magic_wall_time_left = 0;
3839   game.light_time_left = 0;
3840   game.timegate_time_left = 0;
3841   game.switchgate_pos = 0;
3842   game.wind_direction = level.wind_direction_initial;
3843
3844 #if !USE_PLAYER_GRAVITY
3845   game.gravity = FALSE;
3846   game.explosions_delayed = TRUE;
3847 #endif
3848
3849   game.lenses_time_left = 0;
3850   game.magnify_time_left = 0;
3851
3852   game.ball_state = level.ball_state_initial;
3853   game.ball_content_nr = 0;
3854
3855   game.envelope_active = FALSE;
3856
3857   /* set focus to local player for network games, else to all players */
3858   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3859   game.centered_player_nr_next = game.centered_player_nr;
3860   game.set_centered_player = FALSE;
3861
3862   if (network_playing && tape.recording)
3863   {
3864     /* store client dependent player focus when recording network games */
3865     tape.centered_player_nr_next = game.centered_player_nr_next;
3866     tape.set_centered_player = TRUE;
3867   }
3868
3869   for (i = 0; i < NUM_BELTS; i++)
3870   {
3871     game.belt_dir[i] = MV_NONE;
3872     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3873   }
3874
3875   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3876     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3877
3878   SCAN_PLAYFIELD(x, y)
3879   {
3880     Feld[x][y] = level.field[x][y];
3881     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3882     ChangeDelay[x][y] = 0;
3883     ChangePage[x][y] = -1;
3884 #if USE_NEW_CUSTOM_VALUE
3885     CustomValue[x][y] = 0;              /* initialized in InitField() */
3886 #endif
3887     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3888     AmoebaNr[x][y] = 0;
3889     WasJustMoving[x][y] = 0;
3890     WasJustFalling[x][y] = 0;
3891     CheckCollision[x][y] = 0;
3892     CheckImpact[x][y] = 0;
3893     Stop[x][y] = FALSE;
3894     Pushed[x][y] = FALSE;
3895
3896     ChangeCount[x][y] = 0;
3897     ChangeEvent[x][y] = -1;
3898
3899     ExplodePhase[x][y] = 0;
3900     ExplodeDelay[x][y] = 0;
3901     ExplodeField[x][y] = EX_TYPE_NONE;
3902
3903     RunnerVisit[x][y] = 0;
3904     PlayerVisit[x][y] = 0;
3905
3906     GfxFrame[x][y] = 0;
3907     GfxRandom[x][y] = INIT_GFX_RANDOM();
3908     GfxElement[x][y] = EL_UNDEFINED;
3909     GfxAction[x][y] = ACTION_DEFAULT;
3910     GfxDir[x][y] = MV_NONE;
3911     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3912   }
3913
3914   SCAN_PLAYFIELD(x, y)
3915   {
3916     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3917       emulate_bd = FALSE;
3918     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3919       emulate_sb = FALSE;
3920     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3921       emulate_sp = FALSE;
3922
3923     InitField(x, y, TRUE);
3924
3925     ResetGfxAnimation(x, y);
3926   }
3927
3928   InitBeltMovement();
3929
3930   for (i = 0; i < MAX_PLAYERS; i++)
3931   {
3932     struct PlayerInfo *player = &stored_player[i];
3933
3934     /* set number of special actions for bored and sleeping animation */
3935     player->num_special_action_bored =
3936       get_num_special_action(player->artwork_element,
3937                              ACTION_BORING_1, ACTION_BORING_LAST);
3938     player->num_special_action_sleeping =
3939       get_num_special_action(player->artwork_element,
3940                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3941   }
3942
3943   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3944                     emulate_sb ? EMU_SOKOBAN :
3945                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3946
3947 #if USE_NEW_ALL_SLIPPERY
3948   /* initialize type of slippery elements */
3949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3950   {
3951     if (!IS_CUSTOM_ELEMENT(i))
3952     {
3953       /* default: elements slip down either to the left or right randomly */
3954       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3955
3956       /* SP style elements prefer to slip down on the left side */
3957       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3958         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3959
3960       /* BD style elements prefer to slip down on the left side */
3961       if (game.emulation == EMU_BOULDERDASH)
3962         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3963     }
3964   }
3965 #endif
3966
3967   /* initialize explosion and ignition delay */
3968   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3969   {
3970     if (!IS_CUSTOM_ELEMENT(i))
3971     {
3972       int num_phase = 8;
3973       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3974                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3975                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3976       int last_phase = (num_phase + 1) * delay;
3977       int half_phase = (num_phase / 2) * delay;
3978
3979       element_info[i].explosion_delay = last_phase - 1;
3980       element_info[i].ignition_delay = half_phase;
3981
3982       if (i == EL_BLACK_ORB)
3983         element_info[i].ignition_delay = 1;
3984     }
3985
3986 #if 0
3987     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3988       element_info[i].explosion_delay = 1;
3989
3990     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3991       element_info[i].ignition_delay = 1;
3992 #endif
3993   }
3994
3995   /* correct non-moving belts to start moving left */
3996   for (i = 0; i < NUM_BELTS; i++)
3997     if (game.belt_dir[i] == MV_NONE)
3998       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3999
4000   /* check if any connected player was not found in playfield */
4001   for (i = 0; i < MAX_PLAYERS; i++)
4002   {
4003     struct PlayerInfo *player = &stored_player[i];
4004
4005     if (player->connected && !player->present)
4006     {
4007       for (j = 0; j < MAX_PLAYERS; j++)
4008       {
4009         struct PlayerInfo *some_player = &stored_player[j];
4010         int jx = some_player->jx, jy = some_player->jy;
4011
4012         /* assign first free player found that is present in the playfield */
4013         if (some_player->present && !some_player->connected)
4014         {
4015           player->present = TRUE;
4016           player->active = TRUE;
4017
4018           some_player->present = FALSE;
4019           some_player->active = FALSE;
4020
4021           player->initial_element = some_player->initial_element;
4022           player->artwork_element = some_player->artwork_element;
4023
4024           player->block_last_field       = some_player->block_last_field;
4025           player->block_delay_adjustment = some_player->block_delay_adjustment;
4026
4027           StorePlayer[jx][jy] = player->element_nr;
4028           player->jx = player->last_jx = jx;
4029           player->jy = player->last_jy = jy;
4030
4031           break;
4032         }
4033       }
4034     }
4035   }
4036
4037   if (tape.playing)
4038   {
4039     /* when playing a tape, eliminate all players who do not participate */
4040
4041     for (i = 0; i < MAX_PLAYERS; i++)
4042     {
4043       if (stored_player[i].active && !tape.player_participates[i])
4044       {
4045         struct PlayerInfo *player = &stored_player[i];
4046         int jx = player->jx, jy = player->jy;
4047
4048         player->active = FALSE;
4049         StorePlayer[jx][jy] = 0;
4050         Feld[jx][jy] = EL_EMPTY;
4051       }
4052     }
4053   }
4054   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4055   {
4056     /* when in single player mode, eliminate all but the first active player */
4057
4058     for (i = 0; i < MAX_PLAYERS; i++)
4059     {
4060       if (stored_player[i].active)
4061       {
4062         for (j = i + 1; j < MAX_PLAYERS; j++)
4063         {
4064           if (stored_player[j].active)
4065           {
4066             struct PlayerInfo *player = &stored_player[j];
4067             int jx = player->jx, jy = player->jy;
4068
4069             player->active = FALSE;
4070             player->present = FALSE;
4071
4072             StorePlayer[jx][jy] = 0;
4073             Feld[jx][jy] = EL_EMPTY;
4074           }
4075         }
4076       }
4077     }
4078   }
4079
4080   /* when recording the game, store which players take part in the game */
4081   if (tape.recording)
4082   {
4083     for (i = 0; i < MAX_PLAYERS; i++)
4084       if (stored_player[i].active)
4085         tape.player_participates[i] = TRUE;
4086   }
4087
4088   if (options.debug)
4089   {
4090     for (i = 0; i < MAX_PLAYERS; i++)
4091     {
4092       struct PlayerInfo *player = &stored_player[i];
4093
4094       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4095              i+1,
4096              player->present,
4097              player->connected,
4098              player->active);
4099       if (local_player == player)
4100         printf("Player  %d is local player.\n", i+1);
4101     }
4102   }
4103
4104   if (BorderElement == EL_EMPTY)
4105   {
4106     SBX_Left = 0;
4107     SBX_Right = lev_fieldx - SCR_FIELDX;
4108     SBY_Upper = 0;
4109     SBY_Lower = lev_fieldy - SCR_FIELDY;
4110   }
4111   else
4112   {
4113     SBX_Left = -1;
4114     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4115     SBY_Upper = -1;
4116     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4117   }
4118
4119   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4120     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4121
4122   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4123     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4124
4125   /* if local player not found, look for custom element that might create
4126      the player (make some assumptions about the right custom element) */
4127   if (!local_player->present)
4128   {
4129     int start_x = 0, start_y = 0;
4130     int found_rating = 0;
4131     int found_element = EL_UNDEFINED;
4132     int player_nr = local_player->index_nr;
4133
4134     SCAN_PLAYFIELD(x, y)
4135     {
4136       int element = Feld[x][y];
4137       int content;
4138       int xx, yy;
4139       boolean is_player;
4140
4141       if (level.use_start_element[player_nr] &&
4142           level.start_element[player_nr] == element &&
4143           found_rating < 4)
4144       {
4145         start_x = x;
4146         start_y = y;
4147
4148         found_rating = 4;
4149         found_element = element;
4150       }
4151
4152       if (!IS_CUSTOM_ELEMENT(element))
4153         continue;
4154
4155       if (CAN_CHANGE(element))
4156       {
4157         for (i = 0; i < element_info[element].num_change_pages; i++)
4158         {
4159           /* check for player created from custom element as single target */
4160           content = element_info[element].change_page[i].target_element;
4161           is_player = ELEM_IS_PLAYER(content);
4162
4163           if (is_player && (found_rating < 3 ||
4164                             (found_rating == 3 && element < found_element)))
4165           {
4166             start_x = x;
4167             start_y = y;
4168
4169             found_rating = 3;
4170             found_element = element;
4171           }
4172         }
4173       }
4174
4175       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4176       {
4177         /* check for player created from custom element as explosion content */
4178         content = element_info[element].content.e[xx][yy];
4179         is_player = ELEM_IS_PLAYER(content);
4180
4181         if (is_player && (found_rating < 2 ||
4182                           (found_rating == 2 && element < found_element)))
4183         {
4184           start_x = x + xx - 1;
4185           start_y = y + yy - 1;
4186
4187           found_rating = 2;
4188           found_element = element;
4189         }
4190
4191         if (!CAN_CHANGE(element))
4192           continue;
4193
4194         for (i = 0; i < element_info[element].num_change_pages; i++)
4195         {
4196           /* check for player created from custom element as extended target */
4197           content =
4198             element_info[element].change_page[i].target_content.e[xx][yy];
4199
4200           is_player = ELEM_IS_PLAYER(content);
4201
4202           if (is_player && (found_rating < 1 ||
4203                             (found_rating == 1 && element < found_element)))
4204           {
4205             start_x = x + xx - 1;
4206             start_y = y + yy - 1;
4207
4208             found_rating = 1;
4209             found_element = element;
4210           }
4211         }
4212       }
4213     }
4214
4215     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4216                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4217                 start_x - MIDPOSX);
4218
4219     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4220                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4221                 start_y - MIDPOSY);
4222   }
4223   else
4224   {
4225     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4226                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4227                 local_player->jx - MIDPOSX);
4228
4229     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4230                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4231                 local_player->jy - MIDPOSY);
4232   }
4233
4234 #if 0
4235   /* do not use PLAYING mask for fading out from main screen */
4236   game_status = GAME_MODE_MAIN;
4237 #endif
4238
4239   StopAnimation();
4240
4241   if (!game.restart_level)
4242     CloseDoor(DOOR_CLOSE_1);
4243
4244 #if 1
4245   if (level_editor_test_game)
4246     FadeSkipNextFadeIn();
4247   else
4248     FadeSetEnterScreen();
4249 #else
4250   if (level_editor_test_game)
4251     fading = fading_none;
4252   else
4253     fading = menu.destination;
4254 #endif
4255
4256 #if 1
4257   FadeOut(REDRAW_FIELD);
4258 #else
4259   if (do_fading)
4260     FadeOut(REDRAW_FIELD);
4261 #endif
4262
4263 #if 0
4264   game_status = GAME_MODE_PLAYING;
4265 #endif
4266
4267   /* !!! FIX THIS (START) !!! */
4268   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4269   {
4270     InitGameEngine_EM();
4271
4272     /* blit playfield from scroll buffer to normal back buffer for fading in */
4273     BlitScreenToBitmap_EM(backbuffer);
4274   }
4275   else
4276   {
4277     DrawLevel();
4278     DrawAllPlayers();
4279
4280     /* after drawing the level, correct some elements */
4281     if (game.timegate_time_left == 0)
4282       CloseAllOpenTimegates();
4283
4284     /* blit playfield from scroll buffer to normal back buffer for fading in */
4285     if (setup.soft_scrolling)
4286       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4287
4288     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4289   }
4290   /* !!! FIX THIS (END) !!! */
4291
4292 #if 1
4293   FadeIn(REDRAW_FIELD);
4294 #else
4295   if (do_fading)
4296     FadeIn(REDRAW_FIELD);
4297
4298   BackToFront();
4299 #endif
4300
4301   if (!game.restart_level)
4302   {
4303     /* copy default game door content to main double buffer */
4304     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4305                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4306   }
4307
4308   SetPanelBackground();
4309   SetDrawBackgroundMask(REDRAW_DOOR_1);
4310
4311 #if 1
4312   UpdateAndDisplayGameControlValues();
4313 #else
4314   UpdateGameDoorValues();
4315   DrawGameDoorValues();
4316 #endif
4317
4318   if (!game.restart_level)
4319   {
4320     UnmapGameButtons();
4321     UnmapTapeButtons();
4322     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4323     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4324     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4325     MapGameButtons();
4326     MapTapeButtons();
4327
4328     /* copy actual game door content to door double buffer for OpenDoor() */
4329     BlitBitmap(drawto, bitmap_db_door,
4330                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4331
4332     OpenDoor(DOOR_OPEN_ALL);
4333
4334     PlaySound(SND_GAME_STARTING);
4335
4336     if (setup.sound_music)
4337       PlayLevelMusic();
4338
4339     KeyboardAutoRepeatOffUnlessAutoplay();
4340
4341     if (options.debug)
4342     {
4343       for (i = 0; i < MAX_PLAYERS; i++)
4344         printf("Player %d %sactive.\n",
4345                i + 1, (stored_player[i].active ? "" : "not "));
4346     }
4347   }
4348
4349 #if 1
4350   UnmapAllGadgets();
4351
4352   MapGameButtons();
4353   MapTapeButtons();
4354 #endif
4355
4356   game.restart_level = FALSE;
4357 }
4358
4359 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4360 {
4361   /* this is used for non-R'n'D game engines to update certain engine values */
4362
4363   /* needed to determine if sounds are played within the visible screen area */
4364   scroll_x = actual_scroll_x;
4365   scroll_y = actual_scroll_y;
4366 }
4367
4368 void InitMovDir(int x, int y)
4369 {
4370   int i, element = Feld[x][y];
4371   static int xy[4][2] =
4372   {
4373     {  0, +1 },
4374     { +1,  0 },
4375     {  0, -1 },
4376     { -1,  0 }
4377   };
4378   static int direction[3][4] =
4379   {
4380     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4381     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4382     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4383   };
4384
4385   switch (element)
4386   {
4387     case EL_BUG_RIGHT:
4388     case EL_BUG_UP:
4389     case EL_BUG_LEFT:
4390     case EL_BUG_DOWN:
4391       Feld[x][y] = EL_BUG;
4392       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4393       break;
4394
4395     case EL_SPACESHIP_RIGHT:
4396     case EL_SPACESHIP_UP:
4397     case EL_SPACESHIP_LEFT:
4398     case EL_SPACESHIP_DOWN:
4399       Feld[x][y] = EL_SPACESHIP;
4400       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4401       break;
4402
4403     case EL_BD_BUTTERFLY_RIGHT:
4404     case EL_BD_BUTTERFLY_UP:
4405     case EL_BD_BUTTERFLY_LEFT:
4406     case EL_BD_BUTTERFLY_DOWN:
4407       Feld[x][y] = EL_BD_BUTTERFLY;
4408       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4409       break;
4410
4411     case EL_BD_FIREFLY_RIGHT:
4412     case EL_BD_FIREFLY_UP:
4413     case EL_BD_FIREFLY_LEFT:
4414     case EL_BD_FIREFLY_DOWN:
4415       Feld[x][y] = EL_BD_FIREFLY;
4416       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4417       break;
4418
4419     case EL_PACMAN_RIGHT:
4420     case EL_PACMAN_UP:
4421     case EL_PACMAN_LEFT:
4422     case EL_PACMAN_DOWN:
4423       Feld[x][y] = EL_PACMAN;
4424       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4425       break;
4426
4427     case EL_YAMYAM_LEFT:
4428     case EL_YAMYAM_RIGHT:
4429     case EL_YAMYAM_UP:
4430     case EL_YAMYAM_DOWN:
4431       Feld[x][y] = EL_YAMYAM;
4432       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4433       break;
4434
4435     case EL_SP_SNIKSNAK:
4436       MovDir[x][y] = MV_UP;
4437       break;
4438
4439     case EL_SP_ELECTRON:
4440       MovDir[x][y] = MV_LEFT;
4441       break;
4442
4443     case EL_MOLE_LEFT:
4444     case EL_MOLE_RIGHT:
4445     case EL_MOLE_UP:
4446     case EL_MOLE_DOWN:
4447       Feld[x][y] = EL_MOLE;
4448       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4449       break;
4450
4451     default:
4452       if (IS_CUSTOM_ELEMENT(element))
4453       {
4454         struct ElementInfo *ei = &element_info[element];
4455         int move_direction_initial = ei->move_direction_initial;
4456         int move_pattern = ei->move_pattern;
4457
4458         if (move_direction_initial == MV_START_PREVIOUS)
4459         {
4460           if (MovDir[x][y] != MV_NONE)
4461             return;
4462
4463           move_direction_initial = MV_START_AUTOMATIC;
4464         }
4465
4466         if (move_direction_initial == MV_START_RANDOM)
4467           MovDir[x][y] = 1 << RND(4);
4468         else if (move_direction_initial & MV_ANY_DIRECTION)
4469           MovDir[x][y] = move_direction_initial;
4470         else if (move_pattern == MV_ALL_DIRECTIONS ||
4471                  move_pattern == MV_TURNING_LEFT ||
4472                  move_pattern == MV_TURNING_RIGHT ||
4473                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4474                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4475                  move_pattern == MV_TURNING_RANDOM)
4476           MovDir[x][y] = 1 << RND(4);
4477         else if (move_pattern == MV_HORIZONTAL)
4478           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4479         else if (move_pattern == MV_VERTICAL)
4480           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4481         else if (move_pattern & MV_ANY_DIRECTION)
4482           MovDir[x][y] = element_info[element].move_pattern;
4483         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4484                  move_pattern == MV_ALONG_RIGHT_SIDE)
4485         {
4486           /* use random direction as default start direction */
4487           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4488             MovDir[x][y] = 1 << RND(4);
4489
4490           for (i = 0; i < NUM_DIRECTIONS; i++)
4491           {
4492             int x1 = x + xy[i][0];
4493             int y1 = y + xy[i][1];
4494
4495             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4496             {
4497               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4498                 MovDir[x][y] = direction[0][i];
4499               else
4500                 MovDir[x][y] = direction[1][i];
4501
4502               break;
4503             }
4504           }
4505         }                
4506       }
4507       else
4508       {
4509         MovDir[x][y] = 1 << RND(4);
4510
4511         if (element != EL_BUG &&
4512             element != EL_SPACESHIP &&
4513             element != EL_BD_BUTTERFLY &&
4514             element != EL_BD_FIREFLY)
4515           break;
4516
4517         for (i = 0; i < NUM_DIRECTIONS; i++)
4518         {
4519           int x1 = x + xy[i][0];
4520           int y1 = y + xy[i][1];
4521
4522           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4523           {
4524             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4525             {
4526               MovDir[x][y] = direction[0][i];
4527               break;
4528             }
4529             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4530                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4531             {
4532               MovDir[x][y] = direction[1][i];
4533               break;
4534             }
4535           }
4536         }
4537       }
4538       break;
4539   }
4540
4541   GfxDir[x][y] = MovDir[x][y];
4542 }
4543
4544 void InitAmoebaNr(int x, int y)
4545 {
4546   int i;
4547   int group_nr = AmoebeNachbarNr(x, y);
4548
4549   if (group_nr == 0)
4550   {
4551     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4552     {
4553       if (AmoebaCnt[i] == 0)
4554       {
4555         group_nr = i;
4556         break;
4557       }
4558     }
4559   }
4560
4561   AmoebaNr[x][y] = group_nr;
4562   AmoebaCnt[group_nr]++;
4563   AmoebaCnt2[group_nr]++;
4564 }
4565
4566 static void PlayerWins(struct PlayerInfo *player)
4567 {
4568   player->LevelSolved = TRUE;
4569   player->GameOver = TRUE;
4570
4571   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4572                          level.native_em_level->lev->score : player->score);
4573
4574   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4575   player->LevelSolved_CountingScore = player->score_final;
4576 }
4577
4578 void GameWon()
4579 {
4580   static int time, time_final;
4581   static int score, score_final;
4582   static int game_over_delay_1 = 0;
4583   static int game_over_delay_2 = 0;
4584   int game_over_delay_value_1 = 50;
4585   int game_over_delay_value_2 = 50;
4586
4587   if (!local_player->LevelSolved_GameWon)
4588   {
4589     int i;
4590
4591     /* do not start end game actions before the player stops moving (to exit) */
4592     if (local_player->MovPos)
4593       return;
4594
4595     local_player->LevelSolved_GameWon = TRUE;
4596     local_player->LevelSolved_SaveTape = tape.recording;
4597     local_player->LevelSolved_SaveScore = !tape.playing;
4598
4599     if (tape.auto_play)         /* tape might already be stopped here */
4600       tape.auto_play_level_solved = TRUE;
4601
4602 #if 1
4603     TapeStop();
4604 #endif
4605
4606     game_over_delay_1 = game_over_delay_value_1;
4607     game_over_delay_2 = game_over_delay_value_2;
4608
4609     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4610     score = score_final = local_player->score_final;
4611
4612     if (TimeLeft > 0)
4613     {
4614       time_final = 0;
4615       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4616     }
4617     else if (level.time == 0 && TimePlayed < 999)
4618     {
4619       time_final = 999;
4620       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4621     }
4622
4623     local_player->score_final = score_final;
4624
4625     if (level_editor_test_game)
4626     {
4627       time = time_final;
4628       score = score_final;
4629
4630 #if 1
4631       local_player->LevelSolved_CountingTime = time;
4632       local_player->LevelSolved_CountingScore = score;
4633
4634       game_panel_controls[GAME_PANEL_TIME].value = time;
4635       game_panel_controls[GAME_PANEL_SCORE].value = score;
4636
4637       DisplayGameControlValues();
4638 #else
4639       DrawGameValue_Time(time);
4640       DrawGameValue_Score(score);
4641 #endif
4642     }
4643
4644     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4645     {
4646       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4647       {
4648         /* close exit door after last player */
4649         if ((AllPlayersGone &&
4650              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4651               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4652               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4653             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4654             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4655         {
4656           int element = Feld[ExitX][ExitY];
4657
4658 #if 0
4659           if (element == EL_EM_EXIT_OPEN ||
4660               element == EL_EM_STEEL_EXIT_OPEN)
4661           {
4662             Bang(ExitX, ExitY);
4663           }
4664           else
4665 #endif
4666           {
4667             Feld[ExitX][ExitY] =
4668               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4669                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4670                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4671                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4672                EL_EM_STEEL_EXIT_CLOSING);
4673
4674             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4675           }
4676         }
4677
4678         /* player disappears */
4679         DrawLevelField(ExitX, ExitY);
4680       }
4681
4682       for (i = 0; i < MAX_PLAYERS; i++)
4683       {
4684         struct PlayerInfo *player = &stored_player[i];
4685
4686         if (player->present)
4687         {
4688           RemovePlayer(player);
4689
4690           /* player disappears */
4691           DrawLevelField(player->jx, player->jy);
4692         }
4693       }
4694     }
4695
4696     PlaySound(SND_GAME_WINNING);
4697   }
4698
4699   if (game_over_delay_1 > 0)
4700   {
4701     game_over_delay_1--;
4702
4703     return;
4704   }
4705
4706   if (time != time_final)
4707   {
4708     int time_to_go = ABS(time_final - time);
4709     int time_count_dir = (time < time_final ? +1 : -1);
4710     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4711
4712     time  += time_count_steps * time_count_dir;
4713     score += time_count_steps * level.score[SC_TIME_BONUS];
4714
4715 #if 1
4716     local_player->LevelSolved_CountingTime = time;
4717     local_player->LevelSolved_CountingScore = score;
4718
4719     game_panel_controls[GAME_PANEL_TIME].value = time;
4720     game_panel_controls[GAME_PANEL_SCORE].value = score;
4721
4722     DisplayGameControlValues();
4723 #else
4724     DrawGameValue_Time(time);
4725     DrawGameValue_Score(score);
4726 #endif
4727
4728     if (time == time_final)
4729       StopSound(SND_GAME_LEVELTIME_BONUS);
4730     else if (setup.sound_loops)
4731       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4732     else
4733       PlaySound(SND_GAME_LEVELTIME_BONUS);
4734
4735     return;
4736   }
4737
4738   local_player->LevelSolved_PanelOff = TRUE;
4739
4740   if (game_over_delay_2 > 0)
4741   {
4742     game_over_delay_2--;
4743
4744     return;
4745   }
4746
4747 #if 1
4748   GameEnd();
4749 #endif
4750 }
4751
4752 void GameEnd()
4753 {
4754   int hi_pos;
4755   boolean raise_level = FALSE;
4756
4757   local_player->LevelSolved_GameEnd = TRUE;
4758
4759   CloseDoor(DOOR_CLOSE_1);
4760
4761   if (local_player->LevelSolved_SaveTape)
4762   {
4763 #if 0
4764     TapeStop();
4765 #endif
4766
4767 #if 1
4768     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4769 #else
4770     SaveTape(tape.level_nr);            /* ask to save tape */
4771 #endif
4772   }
4773
4774   if (level_editor_test_game)
4775   {
4776     game_status = GAME_MODE_MAIN;
4777
4778 #if 1
4779     DrawAndFadeInMainMenu(REDRAW_FIELD);
4780 #else
4781     DrawMainMenu();
4782 #endif
4783
4784     return;
4785   }
4786
4787   if (!local_player->LevelSolved_SaveScore)
4788   {
4789 #if 1
4790     FadeOut(REDRAW_FIELD);
4791 #endif
4792
4793     game_status = GAME_MODE_MAIN;
4794
4795     DrawAndFadeInMainMenu(REDRAW_FIELD);
4796
4797     return;
4798   }
4799
4800   if (level_nr == leveldir_current->handicap_level)
4801   {
4802     leveldir_current->handicap_level++;
4803     SaveLevelSetup_SeriesInfo();
4804   }
4805
4806   if (level_nr < leveldir_current->last_level)
4807     raise_level = TRUE;                 /* advance to next level */
4808
4809   if ((hi_pos = NewHiScore()) >= 0) 
4810   {
4811     game_status = GAME_MODE_SCORES;
4812
4813     DrawHallOfFame(hi_pos);
4814
4815     if (raise_level)
4816     {
4817       level_nr++;
4818       TapeErase();
4819     }
4820   }
4821   else
4822   {
4823 #if 1
4824     FadeOut(REDRAW_FIELD);
4825 #endif
4826
4827     game_status = GAME_MODE_MAIN;
4828
4829     if (raise_level)
4830     {
4831       level_nr++;
4832       TapeErase();
4833     }
4834
4835     DrawAndFadeInMainMenu(REDRAW_FIELD);
4836   }
4837 }
4838
4839 int NewHiScore()
4840 {
4841   int k, l;
4842   int position = -1;
4843
4844   LoadScore(level_nr);
4845
4846   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4847       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4848     return -1;
4849
4850   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4851   {
4852     if (local_player->score_final > highscore[k].Score)
4853     {
4854       /* player has made it to the hall of fame */
4855
4856       if (k < MAX_SCORE_ENTRIES - 1)
4857       {
4858         int m = MAX_SCORE_ENTRIES - 1;
4859
4860 #ifdef ONE_PER_NAME
4861         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4862           if (strEqual(setup.player_name, highscore[l].Name))
4863             m = l;
4864         if (m == k)     /* player's new highscore overwrites his old one */
4865           goto put_into_list;
4866 #endif
4867
4868         for (l = m; l > k; l--)
4869         {
4870           strcpy(highscore[l].Name, highscore[l - 1].Name);
4871           highscore[l].Score = highscore[l - 1].Score;
4872         }
4873       }
4874
4875 #ifdef ONE_PER_NAME
4876       put_into_list:
4877 #endif
4878       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4879       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4880       highscore[k].Score = local_player->score_final; 
4881       position = k;
4882       break;
4883     }
4884
4885 #ifdef ONE_PER_NAME
4886     else if (!strncmp(setup.player_name, highscore[k].Name,
4887                       MAX_PLAYER_NAME_LEN))
4888       break;    /* player already there with a higher score */
4889 #endif
4890
4891   }
4892
4893   if (position >= 0) 
4894     SaveScore(level_nr);
4895
4896   return position;
4897 }
4898
4899 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4900 {
4901   int element = Feld[x][y];
4902   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4903   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4904   int horiz_move = (dx != 0);
4905   int sign = (horiz_move ? dx : dy);
4906   int step = sign * element_info[element].move_stepsize;
4907
4908   /* special values for move stepsize for spring and things on conveyor belt */
4909   if (horiz_move)
4910   {
4911     if (CAN_FALL(element) &&
4912         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4913       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4914     else if (element == EL_SPRING)
4915       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4916   }
4917
4918   return step;
4919 }
4920
4921 inline static int getElementMoveStepsize(int x, int y)
4922 {
4923   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4924 }
4925
4926 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4927 {
4928   if (player->GfxAction != action || player->GfxDir != dir)
4929   {
4930 #if 0
4931     printf("Player frame reset! (%d => %d, %d => %d)\n",
4932            player->GfxAction, action, player->GfxDir, dir);
4933 #endif
4934
4935     player->GfxAction = action;
4936     player->GfxDir = dir;
4937     player->Frame = 0;
4938     player->StepFrame = 0;
4939   }
4940 }
4941
4942 #if USE_GFX_RESET_GFX_ANIMATION
4943 static void ResetGfxFrame(int x, int y, boolean redraw)
4944 {
4945   int element = Feld[x][y];
4946   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4947   int last_gfx_frame = GfxFrame[x][y];
4948
4949   if (graphic_info[graphic].anim_global_sync)
4950     GfxFrame[x][y] = FrameCounter;
4951   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4952     GfxFrame[x][y] = CustomValue[x][y];
4953   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4954     GfxFrame[x][y] = element_info[element].collect_score;
4955   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4956     GfxFrame[x][y] = ChangeDelay[x][y];
4957
4958   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4959     DrawLevelGraphicAnimation(x, y, graphic);
4960 }
4961 #endif
4962
4963 static void ResetGfxAnimation(int x, int y)
4964 {
4965   GfxAction[x][y] = ACTION_DEFAULT;
4966   GfxDir[x][y] = MovDir[x][y];
4967   GfxFrame[x][y] = 0;
4968
4969 #if USE_GFX_RESET_GFX_ANIMATION
4970   ResetGfxFrame(x, y, FALSE);
4971 #endif
4972 }
4973
4974 static void ResetRandomAnimationValue(int x, int y)
4975 {
4976   GfxRandom[x][y] = INIT_GFX_RANDOM();
4977 }
4978
4979 void InitMovingField(int x, int y, int direction)
4980 {
4981   int element = Feld[x][y];
4982   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4983   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4984   int newx = x + dx;
4985   int newy = y + dy;
4986   boolean is_moving_before, is_moving_after;
4987 #if 0
4988   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4989 #endif
4990
4991   /* check if element was/is moving or being moved before/after mode change */
4992 #if 1
4993 #if 1
4994   is_moving_before = (WasJustMoving[x][y] != 0);
4995 #else
4996   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4997   is_moving_before = WasJustMoving[x][y];
4998 #endif
4999 #else
5000   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5001 #endif
5002   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5003
5004   /* reset animation only for moving elements which change direction of moving
5005      or which just started or stopped moving
5006      (else CEs with property "can move" / "not moving" are reset each frame) */
5007 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5008 #if 1
5009   if (is_moving_before != is_moving_after ||
5010       direction != MovDir[x][y])
5011     ResetGfxAnimation(x, y);
5012 #else
5013   if ((is_moving_before || is_moving_after) && !continues_moving)
5014     ResetGfxAnimation(x, y);
5015 #endif
5016 #else
5017   if (!continues_moving)
5018     ResetGfxAnimation(x, y);
5019 #endif
5020
5021   MovDir[x][y] = direction;
5022   GfxDir[x][y] = direction;
5023
5024 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5025   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5026                      direction == MV_DOWN && CAN_FALL(element) ?
5027                      ACTION_FALLING : ACTION_MOVING);
5028 #else
5029   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5030                      ACTION_FALLING : ACTION_MOVING);
5031 #endif
5032
5033   /* this is needed for CEs with property "can move" / "not moving" */
5034
5035   if (is_moving_after)
5036   {
5037     if (Feld[newx][newy] == EL_EMPTY)
5038       Feld[newx][newy] = EL_BLOCKED;
5039
5040     MovDir[newx][newy] = MovDir[x][y];
5041
5042 #if USE_NEW_CUSTOM_VALUE
5043     CustomValue[newx][newy] = CustomValue[x][y];
5044 #endif
5045
5046     GfxFrame[newx][newy] = GfxFrame[x][y];
5047     GfxRandom[newx][newy] = GfxRandom[x][y];
5048     GfxAction[newx][newy] = GfxAction[x][y];
5049     GfxDir[newx][newy] = GfxDir[x][y];
5050   }
5051 }
5052
5053 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5054 {
5055   int direction = MovDir[x][y];
5056   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5057   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5058
5059   *goes_to_x = newx;
5060   *goes_to_y = newy;
5061 }
5062
5063 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5064 {
5065   int oldx = x, oldy = y;
5066   int direction = MovDir[x][y];
5067
5068   if (direction == MV_LEFT)
5069     oldx++;
5070   else if (direction == MV_RIGHT)
5071     oldx--;
5072   else if (direction == MV_UP)
5073     oldy++;
5074   else if (direction == MV_DOWN)
5075     oldy--;
5076
5077   *comes_from_x = oldx;
5078   *comes_from_y = oldy;
5079 }
5080
5081 int MovingOrBlocked2Element(int x, int y)
5082 {
5083   int element = Feld[x][y];
5084
5085   if (element == EL_BLOCKED)
5086   {
5087     int oldx, oldy;
5088
5089     Blocked2Moving(x, y, &oldx, &oldy);
5090     return Feld[oldx][oldy];
5091   }
5092   else
5093     return element;
5094 }
5095
5096 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5097 {
5098   /* like MovingOrBlocked2Element(), but if element is moving
5099      and (x,y) is the field the moving element is just leaving,
5100      return EL_BLOCKED instead of the element value */
5101   int element = Feld[x][y];
5102
5103   if (IS_MOVING(x, y))
5104   {
5105     if (element == EL_BLOCKED)
5106     {
5107       int oldx, oldy;
5108
5109       Blocked2Moving(x, y, &oldx, &oldy);
5110       return Feld[oldx][oldy];
5111     }
5112     else
5113       return EL_BLOCKED;
5114   }
5115   else
5116     return element;
5117 }
5118
5119 static void RemoveField(int x, int y)
5120 {
5121   Feld[x][y] = EL_EMPTY;
5122
5123   MovPos[x][y] = 0;
5124   MovDir[x][y] = 0;
5125   MovDelay[x][y] = 0;
5126
5127 #if USE_NEW_CUSTOM_VALUE
5128   CustomValue[x][y] = 0;
5129 #endif
5130
5131   AmoebaNr[x][y] = 0;
5132   ChangeDelay[x][y] = 0;
5133   ChangePage[x][y] = -1;
5134   Pushed[x][y] = FALSE;
5135
5136 #if 0
5137   ExplodeField[x][y] = EX_TYPE_NONE;
5138 #endif
5139
5140   GfxElement[x][y] = EL_UNDEFINED;
5141   GfxAction[x][y] = ACTION_DEFAULT;
5142   GfxDir[x][y] = MV_NONE;
5143   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5144 }
5145
5146 void RemoveMovingField(int x, int y)
5147 {
5148   int oldx = x, oldy = y, newx = x, newy = y;
5149   int element = Feld[x][y];
5150   int next_element = EL_UNDEFINED;
5151
5152   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5153     return;
5154
5155   if (IS_MOVING(x, y))
5156   {
5157     Moving2Blocked(x, y, &newx, &newy);
5158
5159     if (Feld[newx][newy] != EL_BLOCKED)
5160     {
5161       /* element is moving, but target field is not free (blocked), but
5162          already occupied by something different (example: acid pool);
5163          in this case, only remove the moving field, but not the target */
5164
5165       RemoveField(oldx, oldy);
5166
5167       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5168
5169       TEST_DrawLevelField(oldx, oldy);
5170
5171       return;
5172     }
5173   }
5174   else if (element == EL_BLOCKED)
5175   {
5176     Blocked2Moving(x, y, &oldx, &oldy);
5177     if (!IS_MOVING(oldx, oldy))
5178       return;
5179   }
5180
5181   if (element == EL_BLOCKED &&
5182       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5183        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5184        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5185        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5186        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5187        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5188     next_element = get_next_element(Feld[oldx][oldy]);
5189
5190   RemoveField(oldx, oldy);
5191   RemoveField(newx, newy);
5192
5193   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5194
5195   if (next_element != EL_UNDEFINED)
5196     Feld[oldx][oldy] = next_element;
5197
5198   TEST_DrawLevelField(oldx, oldy);
5199   TEST_DrawLevelField(newx, newy);
5200 }
5201
5202 void DrawDynamite(int x, int y)
5203 {
5204   int sx = SCREENX(x), sy = SCREENY(y);
5205   int graphic = el2img(Feld[x][y]);
5206   int frame;
5207
5208   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5209     return;
5210
5211   if (IS_WALKABLE_INSIDE(Back[x][y]))
5212     return;
5213
5214   if (Back[x][y])
5215     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5216   else if (Store[x][y])
5217     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5218
5219   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5220
5221   if (Back[x][y] || Store[x][y])
5222     DrawGraphicThruMask(sx, sy, graphic, frame);
5223   else
5224     DrawGraphic(sx, sy, graphic, frame);
5225 }
5226
5227 void CheckDynamite(int x, int y)
5228 {
5229   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5230   {
5231     MovDelay[x][y]--;
5232
5233     if (MovDelay[x][y] != 0)
5234     {
5235       DrawDynamite(x, y);
5236       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5237
5238       return;
5239     }
5240   }
5241
5242   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5243
5244   Bang(x, y);
5245 }
5246
5247 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5248 {
5249   boolean num_checked_players = 0;
5250   int i;
5251
5252   for (i = 0; i < MAX_PLAYERS; i++)
5253   {
5254     if (stored_player[i].active)
5255     {
5256       int sx = stored_player[i].jx;
5257       int sy = stored_player[i].jy;
5258
5259       if (num_checked_players == 0)
5260       {
5261         *sx1 = *sx2 = sx;
5262         *sy1 = *sy2 = sy;
5263       }
5264       else
5265       {
5266         *sx1 = MIN(*sx1, sx);
5267         *sy1 = MIN(*sy1, sy);
5268         *sx2 = MAX(*sx2, sx);
5269         *sy2 = MAX(*sy2, sy);
5270       }
5271
5272       num_checked_players++;
5273     }
5274   }
5275 }
5276
5277 static boolean checkIfAllPlayersFitToScreen_RND()
5278 {
5279   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5280
5281   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5282
5283   return (sx2 - sx1 < SCR_FIELDX &&
5284           sy2 - sy1 < SCR_FIELDY);
5285 }
5286
5287 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5288 {
5289   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5290
5291   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5292
5293   *sx = (sx1 + sx2) / 2;
5294   *sy = (sy1 + sy2) / 2;
5295 }
5296
5297 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5298                         boolean center_screen, boolean quick_relocation)
5299 {
5300   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5301   boolean no_delay = (tape.warp_forward);
5302   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5303   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5304
5305   if (quick_relocation)
5306   {
5307     int offset = game.scroll_delay_value;
5308
5309     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5310     {
5311       if (!level.shifted_relocation || center_screen)
5312       {
5313         /* quick relocation (without scrolling), with centering of screen */
5314
5315         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5316                     x > SBX_Right + MIDPOSX ? SBX_Right :
5317                     x - MIDPOSX);
5318
5319         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5321                     y - MIDPOSY);
5322       }
5323       else
5324       {
5325         /* quick relocation (without scrolling), but do not center screen */
5326
5327         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5328                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5329                                old_x - MIDPOSX);
5330
5331         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5332                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5333                                old_y - MIDPOSY);
5334
5335         int offset_x = x + (scroll_x - center_scroll_x);
5336         int offset_y = y + (scroll_y - center_scroll_y);
5337
5338         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5339                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5340                     offset_x - MIDPOSX);
5341
5342         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5343                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5344                     offset_y - MIDPOSY);
5345       }
5346     }
5347     else
5348     {
5349       /* quick relocation (without scrolling), inside visible screen area */
5350
5351       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5352           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5353         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5354
5355       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5356           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5357         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5358
5359       /* don't scroll over playfield boundaries */
5360       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5361         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5362
5363       /* don't scroll over playfield boundaries */
5364       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5365         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5366     }
5367
5368     RedrawPlayfield(TRUE, 0,0,0,0);
5369   }
5370   else
5371   {
5372 #if 1
5373     int scroll_xx, scroll_yy;
5374
5375     if (!level.shifted_relocation || center_screen)
5376     {
5377       /* visible relocation (with scrolling), with centering of screen */
5378
5379       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5380                    x > SBX_Right + MIDPOSX ? SBX_Right :
5381                    x - MIDPOSX);
5382
5383       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5384                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5385                    y - MIDPOSY);
5386     }
5387     else
5388     {
5389       /* visible relocation (with scrolling), but do not center screen */
5390
5391       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5392                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5393                              old_x - MIDPOSX);
5394
5395       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5396                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5397                              old_y - MIDPOSY);
5398
5399       int offset_x = x + (scroll_x - center_scroll_x);
5400       int offset_y = y + (scroll_y - center_scroll_y);
5401
5402       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5403                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5404                    offset_x - MIDPOSX);
5405
5406       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5407                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5408                    offset_y - MIDPOSY);
5409     }
5410
5411 #else
5412
5413     /* visible relocation (with scrolling), with centering of screen */
5414
5415     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5416                      x > SBX_Right + MIDPOSX ? SBX_Right :
5417                      x - MIDPOSX);
5418
5419     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5420                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5421                      y - MIDPOSY);
5422 #endif
5423
5424     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5425
5426     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5427     {
5428       int dx = 0, dy = 0;
5429       int fx = FX, fy = FY;
5430
5431       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5432       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5433
5434       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5435         break;
5436
5437       scroll_x -= dx;
5438       scroll_y -= dy;
5439
5440       fx += dx * TILEX / 2;
5441       fy += dy * TILEY / 2;
5442
5443       ScrollLevel(dx, dy);
5444       DrawAllPlayers();
5445
5446       /* scroll in two steps of half tile size to make things smoother */
5447       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5448       FlushDisplay();
5449       Delay(wait_delay_value);
5450
5451       /* scroll second step to align at full tile size */
5452       BackToFront();
5453       Delay(wait_delay_value);
5454     }
5455
5456     DrawAllPlayers();
5457     BackToFront();
5458     Delay(wait_delay_value);
5459   }
5460 }
5461
5462 void RelocatePlayer(int jx, int jy, int el_player_raw)
5463 {
5464   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5465   int player_nr = GET_PLAYER_NR(el_player);
5466   struct PlayerInfo *player = &stored_player[player_nr];
5467   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5468   boolean no_delay = (tape.warp_forward);
5469   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5470   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5471   int old_jx = player->jx;
5472   int old_jy = player->jy;
5473   int old_element = Feld[old_jx][old_jy];
5474   int element = Feld[jx][jy];
5475   boolean player_relocated = (old_jx != jx || old_jy != jy);
5476
5477   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5478   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5479   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5480   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5481   int leave_side_horiz = move_dir_horiz;
5482   int leave_side_vert  = move_dir_vert;
5483   int enter_side = enter_side_horiz | enter_side_vert;
5484   int leave_side = leave_side_horiz | leave_side_vert;
5485
5486   if (player->GameOver)         /* do not reanimate dead player */
5487     return;
5488
5489   if (!player_relocated)        /* no need to relocate the player */
5490     return;
5491
5492   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5493   {
5494     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5495     DrawLevelField(jx, jy);
5496   }
5497
5498   if (player->present)
5499   {
5500     while (player->MovPos)
5501     {
5502       ScrollPlayer(player, SCROLL_GO_ON);
5503       ScrollScreen(NULL, SCROLL_GO_ON);
5504
5505       AdvanceFrameAndPlayerCounters(player->index_nr);
5506
5507       DrawPlayer(player);
5508
5509       BackToFront();
5510       Delay(wait_delay_value);
5511     }
5512
5513     DrawPlayer(player);         /* needed here only to cleanup last field */
5514     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5515
5516     player->is_moving = FALSE;
5517   }
5518
5519   if (IS_CUSTOM_ELEMENT(old_element))
5520     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5521                                CE_LEFT_BY_PLAYER,
5522                                player->index_bit, leave_side);
5523
5524   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5525                                       CE_PLAYER_LEAVES_X,
5526                                       player->index_bit, leave_side);
5527
5528   Feld[jx][jy] = el_player;
5529   InitPlayerField(jx, jy, el_player, TRUE);
5530
5531   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5532   {
5533     Feld[jx][jy] = element;
5534     InitField(jx, jy, FALSE);
5535   }
5536
5537   /* only visually relocate centered player */
5538   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5539                      FALSE, level.instant_relocation);
5540
5541   TestIfPlayerTouchesBadThing(jx, jy);
5542   TestIfPlayerTouchesCustomElement(jx, jy);
5543
5544   if (IS_CUSTOM_ELEMENT(element))
5545     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5546                                player->index_bit, enter_side);
5547
5548   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5549                                       player->index_bit, enter_side);
5550 }
5551
5552 void Explode(int ex, int ey, int phase, int mode)
5553 {
5554   int x, y;
5555   int last_phase;
5556   int border_element;
5557
5558   /* !!! eliminate this variable !!! */
5559   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5560
5561   if (game.explosions_delayed)
5562   {
5563     ExplodeField[ex][ey] = mode;
5564     return;
5565   }
5566
5567   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5568   {
5569     int center_element = Feld[ex][ey];
5570     int artwork_element, explosion_element;     /* set these values later */
5571
5572 #if 0
5573     /* --- This is only really needed (and now handled) in "Impact()". --- */
5574     /* do not explode moving elements that left the explode field in time */
5575     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5576         center_element == EL_EMPTY &&
5577         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5578       return;
5579 #endif
5580
5581 #if 0
5582     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5583     if (mode == EX_TYPE_NORMAL ||
5584         mode == EX_TYPE_CENTER ||
5585         mode == EX_TYPE_CROSS)
5586       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5587 #endif
5588
5589     /* remove things displayed in background while burning dynamite */
5590     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5591       Back[ex][ey] = 0;
5592
5593     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5594     {
5595       /* put moving element to center field (and let it explode there) */
5596       center_element = MovingOrBlocked2Element(ex, ey);
5597       RemoveMovingField(ex, ey);
5598       Feld[ex][ey] = center_element;
5599     }
5600
5601     /* now "center_element" is finally determined -- set related values now */
5602     artwork_element = center_element;           /* for custom player artwork */
5603     explosion_element = center_element;         /* for custom player artwork */
5604
5605     if (IS_PLAYER(ex, ey))
5606     {
5607       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5608
5609       artwork_element = stored_player[player_nr].artwork_element;
5610
5611       if (level.use_explosion_element[player_nr])
5612       {
5613         explosion_element = level.explosion_element[player_nr];
5614         artwork_element = explosion_element;
5615       }
5616     }
5617
5618 #if 1
5619     if (mode == EX_TYPE_NORMAL ||
5620         mode == EX_TYPE_CENTER ||
5621         mode == EX_TYPE_CROSS)
5622       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5623 #endif
5624
5625     last_phase = element_info[explosion_element].explosion_delay + 1;
5626
5627     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5628     {
5629       int xx = x - ex + 1;
5630       int yy = y - ey + 1;
5631       int element;
5632
5633       if (!IN_LEV_FIELD(x, y) ||
5634           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5635           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5636         continue;
5637
5638       element = Feld[x][y];
5639
5640       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5641       {
5642         element = MovingOrBlocked2Element(x, y);
5643
5644         if (!IS_EXPLOSION_PROOF(element))
5645           RemoveMovingField(x, y);
5646       }
5647
5648       /* indestructible elements can only explode in center (but not flames) */
5649       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5650                                            mode == EX_TYPE_BORDER)) ||
5651           element == EL_FLAMES)
5652         continue;
5653
5654       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5655          behaviour, for example when touching a yamyam that explodes to rocks
5656          with active deadly shield, a rock is created under the player !!! */
5657       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5658 #if 0
5659       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5660           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5661            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5662 #else
5663       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5664 #endif
5665       {
5666         if (IS_ACTIVE_BOMB(element))
5667         {
5668           /* re-activate things under the bomb like gate or penguin */
5669           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5670           Back[x][y] = 0;
5671         }
5672
5673         continue;
5674       }
5675
5676       /* save walkable background elements while explosion on same tile */
5677       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5678           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5679         Back[x][y] = element;
5680
5681       /* ignite explodable elements reached by other explosion */
5682       if (element == EL_EXPLOSION)
5683         element = Store2[x][y];
5684
5685       if (AmoebaNr[x][y] &&
5686           (element == EL_AMOEBA_FULL ||
5687            element == EL_BD_AMOEBA ||
5688            element == EL_AMOEBA_GROWING))
5689       {
5690         AmoebaCnt[AmoebaNr[x][y]]--;
5691         AmoebaCnt2[AmoebaNr[x][y]]--;
5692       }
5693
5694       RemoveField(x, y);
5695
5696       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5697       {
5698         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5699
5700         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5701
5702         if (PLAYERINFO(ex, ey)->use_murphy)
5703           Store[x][y] = EL_EMPTY;
5704       }
5705
5706       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5707          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5708       else if (ELEM_IS_PLAYER(center_element))
5709         Store[x][y] = EL_EMPTY;
5710       else if (center_element == EL_YAMYAM)
5711         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5712       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5713         Store[x][y] = element_info[center_element].content.e[xx][yy];
5714 #if 1
5715       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5716          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5717          otherwise) -- FIX THIS !!! */
5718       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5719         Store[x][y] = element_info[element].content.e[1][1];
5720 #else
5721       else if (!CAN_EXPLODE(element))
5722         Store[x][y] = element_info[element].content.e[1][1];
5723 #endif
5724       else
5725         Store[x][y] = EL_EMPTY;
5726
5727       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5728           center_element == EL_AMOEBA_TO_DIAMOND)
5729         Store2[x][y] = element;
5730
5731       Feld[x][y] = EL_EXPLOSION;
5732       GfxElement[x][y] = artwork_element;
5733
5734       ExplodePhase[x][y] = 1;
5735       ExplodeDelay[x][y] = last_phase;
5736
5737       Stop[x][y] = TRUE;
5738     }
5739
5740     if (center_element == EL_YAMYAM)
5741       game.yamyam_content_nr =
5742         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5743
5744     return;
5745   }
5746
5747   if (Stop[ex][ey])
5748     return;
5749
5750   x = ex;
5751   y = ey;
5752
5753   if (phase == 1)
5754     GfxFrame[x][y] = 0;         /* restart explosion animation */
5755
5756   last_phase = ExplodeDelay[x][y];
5757
5758   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5759
5760 #ifdef DEBUG
5761
5762   /* activate this even in non-DEBUG version until cause for crash in
5763      getGraphicAnimationFrame() (see below) is found and eliminated */
5764
5765 #endif
5766 #if 1
5767
5768 #if 1
5769   /* this can happen if the player leaves an explosion just in time */
5770   if (GfxElement[x][y] == EL_UNDEFINED)
5771     GfxElement[x][y] = EL_EMPTY;
5772 #else
5773   if (GfxElement[x][y] == EL_UNDEFINED)
5774   {
5775     printf("\n\n");
5776     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5777     printf("Explode(): This should never happen!\n");
5778     printf("\n\n");
5779
5780     GfxElement[x][y] = EL_EMPTY;
5781   }
5782 #endif
5783
5784 #endif
5785
5786   border_element = Store2[x][y];
5787   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5788     border_element = StorePlayer[x][y];
5789
5790   if (phase == element_info[border_element].ignition_delay ||
5791       phase == last_phase)
5792   {
5793     boolean border_explosion = FALSE;
5794
5795     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5796         !PLAYER_EXPLOSION_PROTECTED(x, y))
5797     {
5798       KillPlayerUnlessExplosionProtected(x, y);
5799       border_explosion = TRUE;
5800     }
5801     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5802     {
5803       Feld[x][y] = Store2[x][y];
5804       Store2[x][y] = 0;
5805       Bang(x, y);
5806       border_explosion = TRUE;
5807     }
5808     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5809     {
5810       AmoebeUmwandeln(x, y);
5811       Store2[x][y] = 0;
5812       border_explosion = TRUE;
5813     }
5814
5815     /* if an element just explodes due to another explosion (chain-reaction),
5816        do not immediately end the new explosion when it was the last frame of
5817        the explosion (as it would be done in the following "if"-statement!) */
5818     if (border_explosion && phase == last_phase)
5819       return;
5820   }
5821
5822   if (phase == last_phase)
5823   {
5824     int element;
5825
5826     element = Feld[x][y] = Store[x][y];
5827     Store[x][y] = Store2[x][y] = 0;
5828     GfxElement[x][y] = EL_UNDEFINED;
5829
5830     /* player can escape from explosions and might therefore be still alive */
5831     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5832         element <= EL_PLAYER_IS_EXPLODING_4)
5833     {
5834       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5835       int explosion_element = EL_PLAYER_1 + player_nr;
5836       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5837       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5838
5839       if (level.use_explosion_element[player_nr])
5840         explosion_element = level.explosion_element[player_nr];
5841
5842       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5843                     element_info[explosion_element].content.e[xx][yy]);
5844     }
5845
5846     /* restore probably existing indestructible background element */
5847     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5848       element = Feld[x][y] = Back[x][y];
5849     Back[x][y] = 0;
5850
5851     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5852     GfxDir[x][y] = MV_NONE;
5853     ChangeDelay[x][y] = 0;
5854     ChangePage[x][y] = -1;
5855
5856 #if USE_NEW_CUSTOM_VALUE
5857     CustomValue[x][y] = 0;
5858 #endif
5859
5860     InitField_WithBug2(x, y, FALSE);
5861
5862     TEST_DrawLevelField(x, y);
5863
5864     TestIfElementTouchesCustomElement(x, y);
5865
5866     if (GFX_CRUMBLED(element))
5867       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5868
5869     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5870       StorePlayer[x][y] = 0;
5871
5872     if (ELEM_IS_PLAYER(element))
5873       RelocatePlayer(x, y, element);
5874   }
5875   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5876   {
5877     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5878     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5879
5880     if (phase == delay)
5881       TEST_DrawLevelFieldCrumbledSand(x, y);
5882
5883     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5884     {
5885       DrawLevelElement(x, y, Back[x][y]);
5886       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5887     }
5888     else if (IS_WALKABLE_UNDER(Back[x][y]))
5889     {
5890       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5891       DrawLevelElementThruMask(x, y, Back[x][y]);
5892     }
5893     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5894       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5895   }
5896 }
5897
5898 void DynaExplode(int ex, int ey)
5899 {
5900   int i, j;
5901   int dynabomb_element = Feld[ex][ey];
5902   int dynabomb_size = 1;
5903   boolean dynabomb_xl = FALSE;
5904   struct PlayerInfo *player;
5905   static int xy[4][2] =
5906   {
5907     { 0, -1 },
5908     { -1, 0 },
5909     { +1, 0 },
5910     { 0, +1 }
5911   };
5912
5913   if (IS_ACTIVE_BOMB(dynabomb_element))
5914   {
5915     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5916     dynabomb_size = player->dynabomb_size;
5917     dynabomb_xl = player->dynabomb_xl;
5918     player->dynabombs_left++;
5919   }
5920
5921   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5922
5923   for (i = 0; i < NUM_DIRECTIONS; i++)
5924   {
5925     for (j = 1; j <= dynabomb_size; j++)
5926     {
5927       int x = ex + j * xy[i][0];
5928       int y = ey + j * xy[i][1];
5929       int element;
5930
5931       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5932         break;
5933
5934       element = Feld[x][y];
5935
5936       /* do not restart explosions of fields with active bombs */
5937       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5938         continue;
5939
5940       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5941
5942       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5943           !IS_DIGGABLE(element) && !dynabomb_xl)
5944         break;
5945     }
5946   }
5947 }
5948
5949 void Bang(int x, int y)
5950 {
5951   int element = MovingOrBlocked2Element(x, y);
5952   int explosion_type = EX_TYPE_NORMAL;
5953
5954   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5955   {
5956     struct PlayerInfo *player = PLAYERINFO(x, y);
5957
5958 #if USE_FIX_CE_ACTION_WITH_PLAYER
5959     element = Feld[x][y] = player->initial_element;
5960 #else
5961     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5962                             player->element_nr);
5963 #endif
5964
5965     if (level.use_explosion_element[player->index_nr])
5966     {
5967       int explosion_element = level.explosion_element[player->index_nr];
5968
5969       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5970         explosion_type = EX_TYPE_CROSS;
5971       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5972         explosion_type = EX_TYPE_CENTER;
5973     }
5974   }
5975
5976   switch (element)
5977   {
5978     case EL_BUG:
5979     case EL_SPACESHIP:
5980     case EL_BD_BUTTERFLY:
5981     case EL_BD_FIREFLY:
5982     case EL_YAMYAM:
5983     case EL_DARK_YAMYAM:
5984     case EL_ROBOT:
5985     case EL_PACMAN:
5986     case EL_MOLE:
5987       RaiseScoreElement(element);
5988       break;
5989
5990     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5991     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5992     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5993     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5994     case EL_DYNABOMB_INCREASE_NUMBER:
5995     case EL_DYNABOMB_INCREASE_SIZE:
5996     case EL_DYNABOMB_INCREASE_POWER:
5997       explosion_type = EX_TYPE_DYNA;
5998       break;
5999
6000     case EL_DC_LANDMINE:
6001 #if 0
6002     case EL_EM_EXIT_OPEN:
6003     case EL_EM_STEEL_EXIT_OPEN:
6004 #endif
6005       explosion_type = EX_TYPE_CENTER;
6006       break;
6007
6008     case EL_PENGUIN:
6009     case EL_LAMP:
6010     case EL_LAMP_ACTIVE:
6011     case EL_AMOEBA_TO_DIAMOND:
6012       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6013         explosion_type = EX_TYPE_CENTER;
6014       break;
6015
6016     default:
6017       if (element_info[element].explosion_type == EXPLODES_CROSS)
6018         explosion_type = EX_TYPE_CROSS;
6019       else if (element_info[element].explosion_type == EXPLODES_1X1)
6020         explosion_type = EX_TYPE_CENTER;
6021       break;
6022   }
6023
6024   if (explosion_type == EX_TYPE_DYNA)
6025     DynaExplode(x, y);
6026   else
6027     Explode(x, y, EX_PHASE_START, explosion_type);
6028
6029   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6030 }
6031
6032 void SplashAcid(int x, int y)
6033 {
6034   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6035       (!IN_LEV_FIELD(x - 1, y - 2) ||
6036        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6037     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6038
6039   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6040       (!IN_LEV_FIELD(x + 1, y - 2) ||
6041        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6042     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6043
6044   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6045 }
6046
6047 static void InitBeltMovement()
6048 {
6049   static int belt_base_element[4] =
6050   {
6051     EL_CONVEYOR_BELT_1_LEFT,
6052     EL_CONVEYOR_BELT_2_LEFT,
6053     EL_CONVEYOR_BELT_3_LEFT,
6054     EL_CONVEYOR_BELT_4_LEFT
6055   };
6056   static int belt_base_active_element[4] =
6057   {
6058     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6059     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6060     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6061     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6062   };
6063
6064   int x, y, i, j;
6065
6066   /* set frame order for belt animation graphic according to belt direction */
6067   for (i = 0; i < NUM_BELTS; i++)
6068   {
6069     int belt_nr = i;
6070
6071     for (j = 0; j < NUM_BELT_PARTS; j++)
6072     {
6073       int element = belt_base_active_element[belt_nr] + j;
6074       int graphic_1 = el2img(element);
6075       int graphic_2 = el2panelimg(element);
6076
6077       if (game.belt_dir[i] == MV_LEFT)
6078       {
6079         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6080         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6081       }
6082       else
6083       {
6084         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6085         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6086       }
6087     }
6088   }
6089
6090   SCAN_PLAYFIELD(x, y)
6091   {
6092     int element = Feld[x][y];
6093
6094     for (i = 0; i < NUM_BELTS; i++)
6095     {
6096       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6097       {
6098         int e_belt_nr = getBeltNrFromBeltElement(element);
6099         int belt_nr = i;
6100
6101         if (e_belt_nr == belt_nr)
6102         {
6103           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6104
6105           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6106         }
6107       }
6108     }
6109   }
6110 }
6111
6112 static void ToggleBeltSwitch(int x, int y)
6113 {
6114   static int belt_base_element[4] =
6115   {
6116     EL_CONVEYOR_BELT_1_LEFT,
6117     EL_CONVEYOR_BELT_2_LEFT,
6118     EL_CONVEYOR_BELT_3_LEFT,
6119     EL_CONVEYOR_BELT_4_LEFT
6120   };
6121   static int belt_base_active_element[4] =
6122   {
6123     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6124     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6125     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6126     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6127   };
6128   static int belt_base_switch_element[4] =
6129   {
6130     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6131     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6132     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6133     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6134   };
6135   static int belt_move_dir[4] =
6136   {
6137     MV_LEFT,
6138     MV_NONE,
6139     MV_RIGHT,
6140     MV_NONE,
6141   };
6142
6143   int element = Feld[x][y];
6144   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6145   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6146   int belt_dir = belt_move_dir[belt_dir_nr];
6147   int xx, yy, i;
6148
6149   if (!IS_BELT_SWITCH(element))
6150     return;
6151
6152   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6153   game.belt_dir[belt_nr] = belt_dir;
6154
6155   if (belt_dir_nr == 3)
6156     belt_dir_nr = 1;
6157
6158   /* set frame order for belt animation graphic according to belt direction */
6159   for (i = 0; i < NUM_BELT_PARTS; i++)
6160   {
6161     int element = belt_base_active_element[belt_nr] + i;
6162     int graphic_1 = el2img(element);
6163     int graphic_2 = el2panelimg(element);
6164
6165     if (belt_dir == MV_LEFT)
6166     {
6167       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6168       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6169     }
6170     else
6171     {
6172       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6173       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6174     }
6175   }
6176
6177   SCAN_PLAYFIELD(xx, yy)
6178   {
6179     int element = Feld[xx][yy];
6180
6181     if (IS_BELT_SWITCH(element))
6182     {
6183       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6184
6185       if (e_belt_nr == belt_nr)
6186       {
6187         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6188         TEST_DrawLevelField(xx, yy);
6189       }
6190     }
6191     else if (IS_BELT(element) && belt_dir != MV_NONE)
6192     {
6193       int e_belt_nr = getBeltNrFromBeltElement(element);
6194
6195       if (e_belt_nr == belt_nr)
6196       {
6197         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6198
6199         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6200         TEST_DrawLevelField(xx, yy);
6201       }
6202     }
6203     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6204     {
6205       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6206
6207       if (e_belt_nr == belt_nr)
6208       {
6209         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6210
6211         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6212         TEST_DrawLevelField(xx, yy);
6213       }
6214     }
6215   }
6216 }
6217
6218 static void ToggleSwitchgateSwitch(int x, int y)
6219 {
6220   int xx, yy;
6221
6222   game.switchgate_pos = !game.switchgate_pos;
6223
6224   SCAN_PLAYFIELD(xx, yy)
6225   {
6226     int element = Feld[xx][yy];
6227
6228 #if !USE_BOTH_SWITCHGATE_SWITCHES
6229     if (element == EL_SWITCHGATE_SWITCH_UP ||
6230         element == EL_SWITCHGATE_SWITCH_DOWN)
6231     {
6232       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6233       TEST_DrawLevelField(xx, yy);
6234     }
6235     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6236              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6237     {
6238       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6239       TEST_DrawLevelField(xx, yy);
6240     }
6241 #else
6242     if (element == EL_SWITCHGATE_SWITCH_UP)
6243     {
6244       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6245       TEST_DrawLevelField(xx, yy);
6246     }
6247     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6248     {
6249       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6250       TEST_DrawLevelField(xx, yy);
6251     }
6252     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6253     {
6254       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6255       TEST_DrawLevelField(xx, yy);
6256     }
6257     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6258     {
6259       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6260       TEST_DrawLevelField(xx, yy);
6261     }
6262 #endif
6263     else if (element == EL_SWITCHGATE_OPEN ||
6264              element == EL_SWITCHGATE_OPENING)
6265     {
6266       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6267
6268       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6269     }
6270     else if (element == EL_SWITCHGATE_CLOSED ||
6271              element == EL_SWITCHGATE_CLOSING)
6272     {
6273       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6274
6275       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6276     }
6277   }
6278 }
6279
6280 static int getInvisibleActiveFromInvisibleElement(int element)
6281 {
6282   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6283           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6284           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6285           element);
6286 }
6287
6288 static int getInvisibleFromInvisibleActiveElement(int element)
6289 {
6290   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6291           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6292           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6293           element);
6294 }
6295
6296 static void RedrawAllLightSwitchesAndInvisibleElements()
6297 {
6298   int x, y;
6299
6300   SCAN_PLAYFIELD(x, y)
6301   {
6302     int element = Feld[x][y];
6303
6304     if (element == EL_LIGHT_SWITCH &&
6305         game.light_time_left > 0)
6306     {
6307       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6308       TEST_DrawLevelField(x, y);
6309     }
6310     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6311              game.light_time_left == 0)
6312     {
6313       Feld[x][y] = EL_LIGHT_SWITCH;
6314       TEST_DrawLevelField(x, y);
6315     }
6316     else if (element == EL_EMC_DRIPPER &&
6317              game.light_time_left > 0)
6318     {
6319       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6320       TEST_DrawLevelField(x, y);
6321     }
6322     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6323              game.light_time_left == 0)
6324     {
6325       Feld[x][y] = EL_EMC_DRIPPER;
6326       TEST_DrawLevelField(x, y);
6327     }
6328     else if (element == EL_INVISIBLE_STEELWALL ||
6329              element == EL_INVISIBLE_WALL ||
6330              element == EL_INVISIBLE_SAND)
6331     {
6332       if (game.light_time_left > 0)
6333         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6334
6335       TEST_DrawLevelField(x, y);
6336
6337       /* uncrumble neighbour fields, if needed */
6338       if (element == EL_INVISIBLE_SAND)
6339         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6340     }
6341     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6342              element == EL_INVISIBLE_WALL_ACTIVE ||
6343              element == EL_INVISIBLE_SAND_ACTIVE)
6344     {
6345       if (game.light_time_left == 0)
6346         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6347
6348       TEST_DrawLevelField(x, y);
6349
6350       /* re-crumble neighbour fields, if needed */
6351       if (element == EL_INVISIBLE_SAND)
6352         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6353     }
6354   }
6355 }
6356
6357 static void RedrawAllInvisibleElementsForLenses()
6358 {
6359   int x, y;
6360
6361   SCAN_PLAYFIELD(x, y)
6362   {
6363     int element = Feld[x][y];
6364
6365     if (element == EL_EMC_DRIPPER &&
6366         game.lenses_time_left > 0)
6367     {
6368       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6369       TEST_DrawLevelField(x, y);
6370     }
6371     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6372              game.lenses_time_left == 0)
6373     {
6374       Feld[x][y] = EL_EMC_DRIPPER;
6375       TEST_DrawLevelField(x, y);
6376     }
6377     else if (element == EL_INVISIBLE_STEELWALL ||
6378              element == EL_INVISIBLE_WALL ||
6379              element == EL_INVISIBLE_SAND)
6380     {
6381       if (game.lenses_time_left > 0)
6382         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6383
6384       TEST_DrawLevelField(x, y);
6385
6386       /* uncrumble neighbour fields, if needed */
6387       if (element == EL_INVISIBLE_SAND)
6388         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6389     }
6390     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6391              element == EL_INVISIBLE_WALL_ACTIVE ||
6392              element == EL_INVISIBLE_SAND_ACTIVE)
6393     {
6394       if (game.lenses_time_left == 0)
6395         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6396
6397       TEST_DrawLevelField(x, y);
6398
6399       /* re-crumble neighbour fields, if needed */
6400       if (element == EL_INVISIBLE_SAND)
6401         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6402     }
6403   }
6404 }
6405
6406 static void RedrawAllInvisibleElementsForMagnifier()
6407 {
6408   int x, y;
6409
6410   SCAN_PLAYFIELD(x, y)
6411   {
6412     int element = Feld[x][y];
6413
6414     if (element == EL_EMC_FAKE_GRASS &&
6415         game.magnify_time_left > 0)
6416     {
6417       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6418       TEST_DrawLevelField(x, y);
6419     }
6420     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6421              game.magnify_time_left == 0)
6422     {
6423       Feld[x][y] = EL_EMC_FAKE_GRASS;
6424       TEST_DrawLevelField(x, y);
6425     }
6426     else if (IS_GATE_GRAY(element) &&
6427              game.magnify_time_left > 0)
6428     {
6429       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6430                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6431                     IS_EM_GATE_GRAY(element) ?
6432                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6433                     IS_EMC_GATE_GRAY(element) ?
6434                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6435                     element);
6436       TEST_DrawLevelField(x, y);
6437     }
6438     else if (IS_GATE_GRAY_ACTIVE(element) &&
6439              game.magnify_time_left == 0)
6440     {
6441       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6442                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6443                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6444                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6445                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6446                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6447                     element);
6448       TEST_DrawLevelField(x, y);
6449     }
6450   }
6451 }
6452
6453 static void ToggleLightSwitch(int x, int y)
6454 {
6455   int element = Feld[x][y];
6456
6457   game.light_time_left =
6458     (element == EL_LIGHT_SWITCH ?
6459      level.time_light * FRAMES_PER_SECOND : 0);
6460
6461   RedrawAllLightSwitchesAndInvisibleElements();
6462 }
6463
6464 static void ActivateTimegateSwitch(int x, int y)
6465 {
6466   int xx, yy;
6467
6468   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6469
6470   SCAN_PLAYFIELD(xx, yy)
6471   {
6472     int element = Feld[xx][yy];
6473
6474     if (element == EL_TIMEGATE_CLOSED ||
6475         element == EL_TIMEGATE_CLOSING)
6476     {
6477       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6478       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6479     }
6480
6481     /*
6482     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6483     {
6484       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6485       TEST_DrawLevelField(xx, yy);
6486     }
6487     */
6488
6489   }
6490
6491 #if 1
6492   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6493                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6494 #else
6495   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6496 #endif
6497 }
6498
6499 void Impact(int x, int y)
6500 {
6501   boolean last_line = (y == lev_fieldy - 1);
6502   boolean object_hit = FALSE;
6503   boolean impact = (last_line || object_hit);
6504   int element = Feld[x][y];
6505   int smashed = EL_STEELWALL;
6506
6507   if (!last_line)       /* check if element below was hit */
6508   {
6509     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6510       return;
6511
6512     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6513                                          MovDir[x][y + 1] != MV_DOWN ||
6514                                          MovPos[x][y + 1] <= TILEY / 2));
6515
6516     /* do not smash moving elements that left the smashed field in time */
6517     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6518         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6519       object_hit = FALSE;
6520
6521 #if USE_QUICKSAND_IMPACT_BUGFIX
6522     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6523     {
6524       RemoveMovingField(x, y + 1);
6525       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6526       Feld[x][y + 2] = EL_ROCK;
6527       TEST_DrawLevelField(x, y + 2);
6528
6529       object_hit = TRUE;
6530     }
6531
6532     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6533     {
6534       RemoveMovingField(x, y + 1);
6535       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6536       Feld[x][y + 2] = EL_ROCK;
6537       TEST_DrawLevelField(x, y + 2);
6538
6539       object_hit = TRUE;
6540     }
6541 #endif
6542
6543     if (object_hit)
6544       smashed = MovingOrBlocked2Element(x, y + 1);
6545
6546     impact = (last_line || object_hit);
6547   }
6548
6549   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6550   {
6551     SplashAcid(x, y + 1);
6552     return;
6553   }
6554
6555   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6556   /* only reset graphic animation if graphic really changes after impact */
6557   if (impact &&
6558       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6559   {
6560     ResetGfxAnimation(x, y);
6561     TEST_DrawLevelField(x, y);
6562   }
6563
6564   if (impact && CAN_EXPLODE_IMPACT(element))
6565   {
6566     Bang(x, y);
6567     return;
6568   }
6569   else if (impact && element == EL_PEARL &&
6570            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6571   {
6572     ResetGfxAnimation(x, y);
6573
6574     Feld[x][y] = EL_PEARL_BREAKING;
6575     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6576     return;
6577   }
6578   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6579   {
6580     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6581
6582     return;
6583   }
6584
6585   if (impact && element == EL_AMOEBA_DROP)
6586   {
6587     if (object_hit && IS_PLAYER(x, y + 1))
6588       KillPlayerUnlessEnemyProtected(x, y + 1);
6589     else if (object_hit && smashed == EL_PENGUIN)
6590       Bang(x, y + 1);
6591     else
6592     {
6593       Feld[x][y] = EL_AMOEBA_GROWING;
6594       Store[x][y] = EL_AMOEBA_WET;
6595
6596       ResetRandomAnimationValue(x, y);
6597     }
6598     return;
6599   }
6600
6601   if (object_hit)               /* check which object was hit */
6602   {
6603     if ((CAN_PASS_MAGIC_WALL(element) && 
6604          (smashed == EL_MAGIC_WALL ||
6605           smashed == EL_BD_MAGIC_WALL)) ||
6606         (CAN_PASS_DC_MAGIC_WALL(element) &&
6607          smashed == EL_DC_MAGIC_WALL))
6608     {
6609       int xx, yy;
6610       int activated_magic_wall =
6611         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6612          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6613          EL_DC_MAGIC_WALL_ACTIVE);
6614
6615       /* activate magic wall / mill */
6616       SCAN_PLAYFIELD(xx, yy)
6617       {
6618         if (Feld[xx][yy] == smashed)
6619           Feld[xx][yy] = activated_magic_wall;
6620       }
6621
6622       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6623       game.magic_wall_active = TRUE;
6624
6625       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6626                             SND_MAGIC_WALL_ACTIVATING :
6627                             smashed == EL_BD_MAGIC_WALL ?
6628                             SND_BD_MAGIC_WALL_ACTIVATING :
6629                             SND_DC_MAGIC_WALL_ACTIVATING));
6630     }
6631
6632     if (IS_PLAYER(x, y + 1))
6633     {
6634       if (CAN_SMASH_PLAYER(element))
6635       {
6636         KillPlayerUnlessEnemyProtected(x, y + 1);
6637         return;
6638       }
6639     }
6640     else if (smashed == EL_PENGUIN)
6641     {
6642       if (CAN_SMASH_PLAYER(element))
6643       {
6644         Bang(x, y + 1);
6645         return;
6646       }
6647     }
6648     else if (element == EL_BD_DIAMOND)
6649     {
6650       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6651       {
6652         Bang(x, y + 1);
6653         return;
6654       }
6655     }
6656     else if (((element == EL_SP_INFOTRON ||
6657                element == EL_SP_ZONK) &&
6658               (smashed == EL_SP_SNIKSNAK ||
6659                smashed == EL_SP_ELECTRON ||
6660                smashed == EL_SP_DISK_ORANGE)) ||
6661              (element == EL_SP_INFOTRON &&
6662               smashed == EL_SP_DISK_YELLOW))
6663     {
6664       Bang(x, y + 1);
6665       return;
6666     }
6667     else if (CAN_SMASH_EVERYTHING(element))
6668     {
6669       if (IS_CLASSIC_ENEMY(smashed) ||
6670           CAN_EXPLODE_SMASHED(smashed))
6671       {
6672         Bang(x, y + 1);
6673         return;
6674       }
6675       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6676       {
6677         if (smashed == EL_LAMP ||
6678             smashed == EL_LAMP_ACTIVE)
6679         {
6680           Bang(x, y + 1);
6681           return;
6682         }
6683         else if (smashed == EL_NUT)
6684         {
6685           Feld[x][y + 1] = EL_NUT_BREAKING;
6686           PlayLevelSound(x, y, SND_NUT_BREAKING);
6687           RaiseScoreElement(EL_NUT);
6688           return;
6689         }
6690         else if (smashed == EL_PEARL)
6691         {
6692           ResetGfxAnimation(x, y);
6693
6694           Feld[x][y + 1] = EL_PEARL_BREAKING;
6695           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6696           return;
6697         }
6698         else if (smashed == EL_DIAMOND)
6699         {
6700           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6701           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6702           return;
6703         }
6704         else if (IS_BELT_SWITCH(smashed))
6705         {
6706           ToggleBeltSwitch(x, y + 1);
6707         }
6708         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6709                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6710                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6711                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6712         {
6713           ToggleSwitchgateSwitch(x, y + 1);
6714         }
6715         else if (smashed == EL_LIGHT_SWITCH ||
6716                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6717         {
6718           ToggleLightSwitch(x, y + 1);
6719         }
6720         else
6721         {
6722 #if 0
6723           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6724 #endif
6725
6726           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6727
6728           CheckElementChangeBySide(x, y + 1, smashed, element,
6729                                    CE_SWITCHED, CH_SIDE_TOP);
6730           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6731                                             CH_SIDE_TOP);
6732         }
6733       }
6734       else
6735       {
6736         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6737       }
6738     }
6739   }
6740
6741   /* play sound of magic wall / mill */
6742   if (!last_line &&
6743       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6744        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6745        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6746   {
6747     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6748       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6749     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6750       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6751     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6752       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6753
6754     return;
6755   }
6756
6757   /* play sound of object that hits the ground */
6758   if (last_line || object_hit)
6759     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6760 }
6761
6762 inline static void TurnRoundExt(int x, int y)
6763 {
6764   static struct
6765   {
6766     int dx, dy;
6767   } move_xy[] =
6768   {
6769     {  0,  0 },
6770     { -1,  0 },
6771     { +1,  0 },
6772     {  0,  0 },
6773     {  0, -1 },
6774     {  0,  0 }, { 0, 0 }, { 0, 0 },
6775     {  0, +1 }
6776   };
6777   static struct
6778   {
6779     int left, right, back;
6780   } turn[] =
6781   {
6782     { 0,        0,              0        },
6783     { MV_DOWN,  MV_UP,          MV_RIGHT },
6784     { MV_UP,    MV_DOWN,        MV_LEFT  },
6785     { 0,        0,              0        },
6786     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6787     { 0,        0,              0        },
6788     { 0,        0,              0        },
6789     { 0,        0,              0        },
6790     { MV_RIGHT, MV_LEFT,        MV_UP    }
6791   };
6792
6793   int element = Feld[x][y];
6794   int move_pattern = element_info[element].move_pattern;
6795
6796   int old_move_dir = MovDir[x][y];
6797   int left_dir  = turn[old_move_dir].left;
6798   int right_dir = turn[old_move_dir].right;
6799   int back_dir  = turn[old_move_dir].back;
6800
6801   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6802   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6803   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6804   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6805
6806   int left_x  = x + left_dx,  left_y  = y + left_dy;
6807   int right_x = x + right_dx, right_y = y + right_dy;
6808   int move_x  = x + move_dx,  move_y  = y + move_dy;
6809
6810   int xx, yy;
6811
6812   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6813   {
6814     TestIfBadThingTouchesOtherBadThing(x, y);
6815
6816     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6817       MovDir[x][y] = right_dir;
6818     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6819       MovDir[x][y] = left_dir;
6820
6821     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6822       MovDelay[x][y] = 9;
6823     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6824       MovDelay[x][y] = 1;
6825   }
6826   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6827   {
6828     TestIfBadThingTouchesOtherBadThing(x, y);
6829
6830     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6831       MovDir[x][y] = left_dir;
6832     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6833       MovDir[x][y] = right_dir;
6834
6835     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6836       MovDelay[x][y] = 9;
6837     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6838       MovDelay[x][y] = 1;
6839   }
6840   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6841   {
6842     TestIfBadThingTouchesOtherBadThing(x, y);
6843
6844     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6845       MovDir[x][y] = left_dir;
6846     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6847       MovDir[x][y] = right_dir;
6848
6849     if (MovDir[x][y] != old_move_dir)
6850       MovDelay[x][y] = 9;
6851   }
6852   else if (element == EL_YAMYAM)
6853   {
6854     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6855     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6856
6857     if (can_turn_left && can_turn_right)
6858       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6859     else if (can_turn_left)
6860       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6861     else if (can_turn_right)
6862       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6863     else
6864       MovDir[x][y] = back_dir;
6865
6866     MovDelay[x][y] = 16 + 16 * RND(3);
6867   }
6868   else if (element == EL_DARK_YAMYAM)
6869   {
6870     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6871                                                          left_x, left_y);
6872     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6873                                                          right_x, right_y);
6874
6875     if (can_turn_left && can_turn_right)
6876       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6877     else if (can_turn_left)
6878       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6879     else if (can_turn_right)
6880       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6881     else
6882       MovDir[x][y] = back_dir;
6883
6884     MovDelay[x][y] = 16 + 16 * RND(3);
6885   }
6886   else if (element == EL_PACMAN)
6887   {
6888     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6889     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6890
6891     if (can_turn_left && can_turn_right)
6892       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6893     else if (can_turn_left)
6894       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6895     else if (can_turn_right)
6896       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6897     else
6898       MovDir[x][y] = back_dir;
6899
6900     MovDelay[x][y] = 6 + RND(40);
6901   }
6902   else if (element == EL_PIG)
6903   {
6904     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6905     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6906     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6907     boolean should_turn_left, should_turn_right, should_move_on;
6908     int rnd_value = 24;
6909     int rnd = RND(rnd_value);
6910
6911     should_turn_left = (can_turn_left &&
6912                         (!can_move_on ||
6913                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6914                                                    y + back_dy + left_dy)));
6915     should_turn_right = (can_turn_right &&
6916                          (!can_move_on ||
6917                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6918                                                     y + back_dy + right_dy)));
6919     should_move_on = (can_move_on &&
6920                       (!can_turn_left ||
6921                        !can_turn_right ||
6922                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6923                                                  y + move_dy + left_dy) ||
6924                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6925                                                  y + move_dy + right_dy)));
6926
6927     if (should_turn_left || should_turn_right || should_move_on)
6928     {
6929       if (should_turn_left && should_turn_right && should_move_on)
6930         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6931                         rnd < 2 * rnd_value / 3 ? right_dir :
6932                         old_move_dir);
6933       else if (should_turn_left && should_turn_right)
6934         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6935       else if (should_turn_left && should_move_on)
6936         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6937       else if (should_turn_right && should_move_on)
6938         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6939       else if (should_turn_left)
6940         MovDir[x][y] = left_dir;
6941       else if (should_turn_right)
6942         MovDir[x][y] = right_dir;
6943       else if (should_move_on)
6944         MovDir[x][y] = old_move_dir;
6945     }
6946     else if (can_move_on && rnd > rnd_value / 8)
6947       MovDir[x][y] = old_move_dir;
6948     else if (can_turn_left && can_turn_right)
6949       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6950     else if (can_turn_left && rnd > rnd_value / 8)
6951       MovDir[x][y] = left_dir;
6952     else if (can_turn_right && rnd > rnd_value/8)
6953       MovDir[x][y] = right_dir;
6954     else
6955       MovDir[x][y] = back_dir;
6956
6957     xx = x + move_xy[MovDir[x][y]].dx;
6958     yy = y + move_xy[MovDir[x][y]].dy;
6959
6960     if (!IN_LEV_FIELD(xx, yy) ||
6961         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6962       MovDir[x][y] = old_move_dir;
6963
6964     MovDelay[x][y] = 0;
6965   }
6966   else if (element == EL_DRAGON)
6967   {
6968     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6969     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6970     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6971     int rnd_value = 24;
6972     int rnd = RND(rnd_value);
6973
6974     if (can_move_on && rnd > rnd_value / 8)
6975       MovDir[x][y] = old_move_dir;
6976     else if (can_turn_left && can_turn_right)
6977       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6978     else if (can_turn_left && rnd > rnd_value / 8)
6979       MovDir[x][y] = left_dir;
6980     else if (can_turn_right && rnd > rnd_value / 8)
6981       MovDir[x][y] = right_dir;
6982     else
6983       MovDir[x][y] = back_dir;
6984
6985     xx = x + move_xy[MovDir[x][y]].dx;
6986     yy = y + move_xy[MovDir[x][y]].dy;
6987
6988     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6989       MovDir[x][y] = old_move_dir;
6990
6991     MovDelay[x][y] = 0;
6992   }
6993   else if (element == EL_MOLE)
6994   {
6995     boolean can_move_on =
6996       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6997                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6998                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6999     if (!can_move_on)
7000     {
7001       boolean can_turn_left =
7002         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7003                               IS_AMOEBOID(Feld[left_x][left_y])));
7004
7005       boolean can_turn_right =
7006         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7007                               IS_AMOEBOID(Feld[right_x][right_y])));
7008
7009       if (can_turn_left && can_turn_right)
7010         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7011       else if (can_turn_left)
7012         MovDir[x][y] = left_dir;
7013       else
7014         MovDir[x][y] = right_dir;
7015     }
7016
7017     if (MovDir[x][y] != old_move_dir)
7018       MovDelay[x][y] = 9;
7019   }
7020   else if (element == EL_BALLOON)
7021   {
7022     MovDir[x][y] = game.wind_direction;
7023     MovDelay[x][y] = 0;
7024   }
7025   else if (element == EL_SPRING)
7026   {
7027 #if USE_NEW_SPRING_BUMPER
7028     if (MovDir[x][y] & MV_HORIZONTAL)
7029     {
7030       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7031           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7032       {
7033         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7034         ResetGfxAnimation(move_x, move_y);
7035         TEST_DrawLevelField(move_x, move_y);
7036
7037         MovDir[x][y] = back_dir;
7038       }
7039       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7040                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7041         MovDir[x][y] = MV_NONE;
7042     }
7043 #else
7044     if (MovDir[x][y] & MV_HORIZONTAL &&
7045         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7046          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7047       MovDir[x][y] = MV_NONE;
7048 #endif
7049
7050     MovDelay[x][y] = 0;
7051   }
7052   else if (element == EL_ROBOT ||
7053            element == EL_SATELLITE ||
7054            element == EL_PENGUIN ||
7055            element == EL_EMC_ANDROID)
7056   {
7057     int attr_x = -1, attr_y = -1;
7058
7059     if (AllPlayersGone)
7060     {
7061       attr_x = ExitX;
7062       attr_y = ExitY;
7063     }
7064     else
7065     {
7066       int i;
7067
7068       for (i = 0; i < MAX_PLAYERS; i++)
7069       {
7070         struct PlayerInfo *player = &stored_player[i];
7071         int jx = player->jx, jy = player->jy;
7072
7073         if (!player->active)
7074           continue;
7075
7076         if (attr_x == -1 ||
7077             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7078         {
7079           attr_x = jx;
7080           attr_y = jy;
7081         }
7082       }
7083     }
7084
7085     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7086         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7087          game.engine_version < VERSION_IDENT(3,1,0,0)))
7088     {
7089       attr_x = ZX;
7090       attr_y = ZY;
7091     }
7092
7093     if (element == EL_PENGUIN)
7094     {
7095       int i;
7096       static int xy[4][2] =
7097       {
7098         { 0, -1 },
7099         { -1, 0 },
7100         { +1, 0 },
7101         { 0, +1 }
7102       };
7103
7104       for (i = 0; i < NUM_DIRECTIONS; i++)
7105       {
7106         int ex = x + xy[i][0];
7107         int ey = y + xy[i][1];
7108
7109         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7110                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7111                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7112                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7113         {
7114           attr_x = ex;
7115           attr_y = ey;
7116           break;
7117         }
7118       }
7119     }
7120
7121     MovDir[x][y] = MV_NONE;
7122     if (attr_x < x)
7123       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7124     else if (attr_x > x)
7125       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7126     if (attr_y < y)
7127       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7128     else if (attr_y > y)
7129       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7130
7131     if (element == EL_ROBOT)
7132     {
7133       int newx, newy;
7134
7135       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7136         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7137       Moving2Blocked(x, y, &newx, &newy);
7138
7139       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7140         MovDelay[x][y] = 8 + 8 * !RND(3);
7141       else
7142         MovDelay[x][y] = 16;
7143     }
7144     else if (element == EL_PENGUIN)
7145     {
7146       int newx, newy;
7147
7148       MovDelay[x][y] = 1;
7149
7150       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7151       {
7152         boolean first_horiz = RND(2);
7153         int new_move_dir = MovDir[x][y];
7154
7155         MovDir[x][y] =
7156           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7157         Moving2Blocked(x, y, &newx, &newy);
7158
7159         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7160           return;
7161
7162         MovDir[x][y] =
7163           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7164         Moving2Blocked(x, y, &newx, &newy);
7165
7166         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7167           return;
7168
7169         MovDir[x][y] = old_move_dir;
7170         return;
7171       }
7172     }
7173     else if (element == EL_SATELLITE)
7174     {
7175       int newx, newy;
7176
7177       MovDelay[x][y] = 1;
7178
7179       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7180       {
7181         boolean first_horiz = RND(2);
7182         int new_move_dir = MovDir[x][y];
7183
7184         MovDir[x][y] =
7185           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7186         Moving2Blocked(x, y, &newx, &newy);
7187
7188         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7189           return;
7190
7191         MovDir[x][y] =
7192           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7193         Moving2Blocked(x, y, &newx, &newy);
7194
7195         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7196           return;
7197
7198         MovDir[x][y] = old_move_dir;
7199         return;
7200       }
7201     }
7202     else if (element == EL_EMC_ANDROID)
7203     {
7204       static int check_pos[16] =
7205       {
7206         -1,             /*  0 => (invalid)          */
7207         7,              /*  1 => MV_LEFT            */
7208         3,              /*  2 => MV_RIGHT           */
7209         -1,             /*  3 => (invalid)          */
7210         1,              /*  4 =>            MV_UP   */
7211         0,              /*  5 => MV_LEFT  | MV_UP   */
7212         2,              /*  6 => MV_RIGHT | MV_UP   */
7213         -1,             /*  7 => (invalid)          */
7214         5,              /*  8 =>            MV_DOWN */
7215         6,              /*  9 => MV_LEFT  | MV_DOWN */
7216         4,              /* 10 => MV_RIGHT | MV_DOWN */
7217         -1,             /* 11 => (invalid)          */
7218         -1,             /* 12 => (invalid)          */
7219         -1,             /* 13 => (invalid)          */
7220         -1,             /* 14 => (invalid)          */
7221         -1,             /* 15 => (invalid)          */
7222       };
7223       static struct
7224       {
7225         int dx, dy;
7226         int dir;
7227       } check_xy[8] =
7228       {
7229         { -1, -1,       MV_LEFT  | MV_UP   },
7230         {  0, -1,                  MV_UP   },
7231         { +1, -1,       MV_RIGHT | MV_UP   },
7232         { +1,  0,       MV_RIGHT           },
7233         { +1, +1,       MV_RIGHT | MV_DOWN },
7234         {  0, +1,                  MV_DOWN },
7235         { -1, +1,       MV_LEFT  | MV_DOWN },
7236         { -1,  0,       MV_LEFT            },
7237       };
7238       int start_pos, check_order;
7239       boolean can_clone = FALSE;
7240       int i;
7241
7242       /* check if there is any free field around current position */
7243       for (i = 0; i < 8; i++)
7244       {
7245         int newx = x + check_xy[i].dx;
7246         int newy = y + check_xy[i].dy;
7247
7248         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7249         {
7250           can_clone = TRUE;
7251
7252           break;
7253         }
7254       }
7255
7256       if (can_clone)            /* randomly find an element to clone */
7257       {
7258         can_clone = FALSE;
7259
7260         start_pos = check_pos[RND(8)];
7261         check_order = (RND(2) ? -1 : +1);
7262
7263         for (i = 0; i < 8; i++)
7264         {
7265           int pos_raw = start_pos + i * check_order;
7266           int pos = (pos_raw + 8) % 8;
7267           int newx = x + check_xy[pos].dx;
7268           int newy = y + check_xy[pos].dy;
7269
7270           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7271           {
7272             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7273             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7274
7275             Store[x][y] = Feld[newx][newy];
7276
7277             can_clone = TRUE;
7278
7279             break;
7280           }
7281         }
7282       }
7283
7284       if (can_clone)            /* randomly find a direction to move */
7285       {
7286         can_clone = FALSE;
7287
7288         start_pos = check_pos[RND(8)];
7289         check_order = (RND(2) ? -1 : +1);
7290
7291         for (i = 0; i < 8; i++)
7292         {
7293           int pos_raw = start_pos + i * check_order;
7294           int pos = (pos_raw + 8) % 8;
7295           int newx = x + check_xy[pos].dx;
7296           int newy = y + check_xy[pos].dy;
7297           int new_move_dir = check_xy[pos].dir;
7298
7299           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7300           {
7301             MovDir[x][y] = new_move_dir;
7302             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7303
7304             can_clone = TRUE;
7305
7306             break;
7307           }
7308         }
7309       }
7310
7311       if (can_clone)            /* cloning and moving successful */
7312         return;
7313
7314       /* cannot clone -- try to move towards player */
7315
7316       start_pos = check_pos[MovDir[x][y] & 0x0f];
7317       check_order = (RND(2) ? -1 : +1);
7318
7319       for (i = 0; i < 3; i++)
7320       {
7321         /* first check start_pos, then previous/next or (next/previous) pos */
7322         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7323         int pos = (pos_raw + 8) % 8;
7324         int newx = x + check_xy[pos].dx;
7325         int newy = y + check_xy[pos].dy;
7326         int new_move_dir = check_xy[pos].dir;
7327
7328         if (IS_PLAYER(newx, newy))
7329           break;
7330
7331         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7332         {
7333           MovDir[x][y] = new_move_dir;
7334           MovDelay[x][y] = level.android_move_time * 8 + 1;
7335
7336           break;
7337         }
7338       }
7339     }
7340   }
7341   else if (move_pattern == MV_TURNING_LEFT ||
7342            move_pattern == MV_TURNING_RIGHT ||
7343            move_pattern == MV_TURNING_LEFT_RIGHT ||
7344            move_pattern == MV_TURNING_RIGHT_LEFT ||
7345            move_pattern == MV_TURNING_RANDOM ||
7346            move_pattern == MV_ALL_DIRECTIONS)
7347   {
7348     boolean can_turn_left =
7349       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7350     boolean can_turn_right =
7351       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7352
7353     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7354       return;
7355
7356     if (move_pattern == MV_TURNING_LEFT)
7357       MovDir[x][y] = left_dir;
7358     else if (move_pattern == MV_TURNING_RIGHT)
7359       MovDir[x][y] = right_dir;
7360     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7361       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7362     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7363       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7364     else if (move_pattern == MV_TURNING_RANDOM)
7365       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7366                       can_turn_right && !can_turn_left ? right_dir :
7367                       RND(2) ? left_dir : right_dir);
7368     else if (can_turn_left && can_turn_right)
7369       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7370     else if (can_turn_left)
7371       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7372     else if (can_turn_right)
7373       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7374     else
7375       MovDir[x][y] = back_dir;
7376
7377     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7378   }
7379   else if (move_pattern == MV_HORIZONTAL ||
7380            move_pattern == MV_VERTICAL)
7381   {
7382     if (move_pattern & old_move_dir)
7383       MovDir[x][y] = back_dir;
7384     else if (move_pattern == MV_HORIZONTAL)
7385       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7386     else if (move_pattern == MV_VERTICAL)
7387       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7388
7389     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7390   }
7391   else if (move_pattern & MV_ANY_DIRECTION)
7392   {
7393     MovDir[x][y] = move_pattern;
7394     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7395   }
7396   else if (move_pattern & MV_WIND_DIRECTION)
7397   {
7398     MovDir[x][y] = game.wind_direction;
7399     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400   }
7401   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7402   {
7403     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7404       MovDir[x][y] = left_dir;
7405     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7406       MovDir[x][y] = right_dir;
7407
7408     if (MovDir[x][y] != old_move_dir)
7409       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7410   }
7411   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7412   {
7413     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7414       MovDir[x][y] = right_dir;
7415     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = left_dir;
7417
7418     if (MovDir[x][y] != old_move_dir)
7419       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7420   }
7421   else if (move_pattern == MV_TOWARDS_PLAYER ||
7422            move_pattern == MV_AWAY_FROM_PLAYER)
7423   {
7424     int attr_x = -1, attr_y = -1;
7425     int newx, newy;
7426     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7427
7428     if (AllPlayersGone)
7429     {
7430       attr_x = ExitX;
7431       attr_y = ExitY;
7432     }
7433     else
7434     {
7435       int i;
7436
7437       for (i = 0; i < MAX_PLAYERS; i++)
7438       {
7439         struct PlayerInfo *player = &stored_player[i];
7440         int jx = player->jx, jy = player->jy;
7441
7442         if (!player->active)
7443           continue;
7444
7445         if (attr_x == -1 ||
7446             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7447         {
7448           attr_x = jx;
7449           attr_y = jy;
7450         }
7451       }
7452     }
7453
7454     MovDir[x][y] = MV_NONE;
7455     if (attr_x < x)
7456       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7457     else if (attr_x > x)
7458       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7459     if (attr_y < y)
7460       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7461     else if (attr_y > y)
7462       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7463
7464     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7465
7466     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7467     {
7468       boolean first_horiz = RND(2);
7469       int new_move_dir = MovDir[x][y];
7470
7471       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7472       {
7473         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7474         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7475
7476         return;
7477       }
7478
7479       MovDir[x][y] =
7480         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7481       Moving2Blocked(x, y, &newx, &newy);
7482
7483       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7484         return;
7485
7486       MovDir[x][y] =
7487         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7488       Moving2Blocked(x, y, &newx, &newy);
7489
7490       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7491         return;
7492
7493       MovDir[x][y] = old_move_dir;
7494     }
7495   }
7496   else if (move_pattern == MV_WHEN_PUSHED ||
7497            move_pattern == MV_WHEN_DROPPED)
7498   {
7499     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7500       MovDir[x][y] = MV_NONE;
7501
7502     MovDelay[x][y] = 0;
7503   }
7504   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7505   {
7506     static int test_xy[7][2] =
7507     {
7508       { 0, -1 },
7509       { -1, 0 },
7510       { +1, 0 },
7511       { 0, +1 },
7512       { 0, -1 },
7513       { -1, 0 },
7514       { +1, 0 },
7515     };
7516     static int test_dir[7] =
7517     {
7518       MV_UP,
7519       MV_LEFT,
7520       MV_RIGHT,
7521       MV_DOWN,
7522       MV_UP,
7523       MV_LEFT,
7524       MV_RIGHT,
7525     };
7526     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7527     int move_preference = -1000000;     /* start with very low preference */
7528     int new_move_dir = MV_NONE;
7529     int start_test = RND(4);
7530     int i;
7531
7532     for (i = 0; i < NUM_DIRECTIONS; i++)
7533     {
7534       int move_dir = test_dir[start_test + i];
7535       int move_dir_preference;
7536
7537       xx = x + test_xy[start_test + i][0];
7538       yy = y + test_xy[start_test + i][1];
7539
7540       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7541           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7542       {
7543         new_move_dir = move_dir;
7544
7545         break;
7546       }
7547
7548       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7549         continue;
7550
7551       move_dir_preference = -1 * RunnerVisit[xx][yy];
7552       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7553         move_dir_preference = PlayerVisit[xx][yy];
7554
7555       if (move_dir_preference > move_preference)
7556       {
7557         /* prefer field that has not been visited for the longest time */
7558         move_preference = move_dir_preference;
7559         new_move_dir = move_dir;
7560       }
7561       else if (move_dir_preference == move_preference &&
7562                move_dir == old_move_dir)
7563       {
7564         /* prefer last direction when all directions are preferred equally */
7565         move_preference = move_dir_preference;
7566         new_move_dir = move_dir;
7567       }
7568     }
7569
7570     MovDir[x][y] = new_move_dir;
7571     if (old_move_dir != new_move_dir)
7572       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7573   }
7574 }
7575
7576 static void TurnRound(int x, int y)
7577 {
7578   int direction = MovDir[x][y];
7579
7580   TurnRoundExt(x, y);
7581
7582   GfxDir[x][y] = MovDir[x][y];
7583
7584   if (direction != MovDir[x][y])
7585     GfxFrame[x][y] = 0;
7586
7587   if (MovDelay[x][y])
7588     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7589
7590   ResetGfxFrame(x, y, FALSE);
7591 }
7592
7593 static boolean JustBeingPushed(int x, int y)
7594 {
7595   int i;
7596
7597   for (i = 0; i < MAX_PLAYERS; i++)
7598   {
7599     struct PlayerInfo *player = &stored_player[i];
7600
7601     if (player->active && player->is_pushing && player->MovPos)
7602     {
7603       int next_jx = player->jx + (player->jx - player->last_jx);
7604       int next_jy = player->jy + (player->jy - player->last_jy);
7605
7606       if (x == next_jx && y == next_jy)
7607         return TRUE;
7608     }
7609   }
7610
7611   return FALSE;
7612 }
7613
7614 void StartMoving(int x, int y)
7615 {
7616   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7617   int element = Feld[x][y];
7618
7619   if (Stop[x][y])
7620     return;
7621
7622   if (MovDelay[x][y] == 0)
7623     GfxAction[x][y] = ACTION_DEFAULT;
7624
7625   if (CAN_FALL(element) && y < lev_fieldy - 1)
7626   {
7627     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7628         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7629       if (JustBeingPushed(x, y))
7630         return;
7631
7632     if (element == EL_QUICKSAND_FULL)
7633     {
7634       if (IS_FREE(x, y + 1))
7635       {
7636         InitMovingField(x, y, MV_DOWN);
7637         started_moving = TRUE;
7638
7639         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7640 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7641         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7642           Store[x][y] = EL_ROCK;
7643 #else
7644         Store[x][y] = EL_ROCK;
7645 #endif
7646
7647         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7648       }
7649       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7650       {
7651         if (!MovDelay[x][y])
7652         {
7653           MovDelay[x][y] = TILEY + 1;
7654
7655           ResetGfxAnimation(x, y);
7656           ResetGfxAnimation(x, y + 1);
7657         }
7658
7659         if (MovDelay[x][y])
7660         {
7661           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7662           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7663
7664           MovDelay[x][y]--;
7665           if (MovDelay[x][y])
7666             return;
7667         }
7668
7669         Feld[x][y] = EL_QUICKSAND_EMPTY;
7670         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7671         Store[x][y + 1] = Store[x][y];
7672         Store[x][y] = 0;
7673
7674         PlayLevelSoundAction(x, y, ACTION_FILLING);
7675       }
7676       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7677       {
7678         if (!MovDelay[x][y])
7679         {
7680           MovDelay[x][y] = TILEY + 1;
7681
7682           ResetGfxAnimation(x, y);
7683           ResetGfxAnimation(x, y + 1);
7684         }
7685
7686         if (MovDelay[x][y])
7687         {
7688           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7689           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7690
7691           MovDelay[x][y]--;
7692           if (MovDelay[x][y])
7693             return;
7694         }
7695
7696         Feld[x][y] = EL_QUICKSAND_EMPTY;
7697         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7698         Store[x][y + 1] = Store[x][y];
7699         Store[x][y] = 0;
7700
7701         PlayLevelSoundAction(x, y, ACTION_FILLING);
7702       }
7703     }
7704     else if (element == EL_QUICKSAND_FAST_FULL)
7705     {
7706       if (IS_FREE(x, y + 1))
7707       {
7708         InitMovingField(x, y, MV_DOWN);
7709         started_moving = TRUE;
7710
7711         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7712 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7713         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7714           Store[x][y] = EL_ROCK;
7715 #else
7716         Store[x][y] = EL_ROCK;
7717 #endif
7718
7719         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7720       }
7721       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7722       {
7723         if (!MovDelay[x][y])
7724         {
7725           MovDelay[x][y] = TILEY + 1;
7726
7727           ResetGfxAnimation(x, y);
7728           ResetGfxAnimation(x, y + 1);
7729         }
7730
7731         if (MovDelay[x][y])
7732         {
7733           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7734           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7735
7736           MovDelay[x][y]--;
7737           if (MovDelay[x][y])
7738             return;
7739         }
7740
7741         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7742         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7743         Store[x][y + 1] = Store[x][y];
7744         Store[x][y] = 0;
7745
7746         PlayLevelSoundAction(x, y, ACTION_FILLING);
7747       }
7748       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7749       {
7750         if (!MovDelay[x][y])
7751         {
7752           MovDelay[x][y] = TILEY + 1;
7753
7754           ResetGfxAnimation(x, y);
7755           ResetGfxAnimation(x, y + 1);
7756         }
7757
7758         if (MovDelay[x][y])
7759         {
7760           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7761           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7762
7763           MovDelay[x][y]--;
7764           if (MovDelay[x][y])
7765             return;
7766         }
7767
7768         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7769         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7770         Store[x][y + 1] = Store[x][y];
7771         Store[x][y] = 0;
7772
7773         PlayLevelSoundAction(x, y, ACTION_FILLING);
7774       }
7775     }
7776     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7777              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7778     {
7779       InitMovingField(x, y, MV_DOWN);
7780       started_moving = TRUE;
7781
7782       Feld[x][y] = EL_QUICKSAND_FILLING;
7783       Store[x][y] = element;
7784
7785       PlayLevelSoundAction(x, y, ACTION_FILLING);
7786     }
7787     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7788              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7789     {
7790       InitMovingField(x, y, MV_DOWN);
7791       started_moving = TRUE;
7792
7793       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7794       Store[x][y] = element;
7795
7796       PlayLevelSoundAction(x, y, ACTION_FILLING);
7797     }
7798     else if (element == EL_MAGIC_WALL_FULL)
7799     {
7800       if (IS_FREE(x, y + 1))
7801       {
7802         InitMovingField(x, y, MV_DOWN);
7803         started_moving = TRUE;
7804
7805         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7806         Store[x][y] = EL_CHANGED(Store[x][y]);
7807       }
7808       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7809       {
7810         if (!MovDelay[x][y])
7811           MovDelay[x][y] = TILEY/4 + 1;
7812
7813         if (MovDelay[x][y])
7814         {
7815           MovDelay[x][y]--;
7816           if (MovDelay[x][y])
7817             return;
7818         }
7819
7820         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7821         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7822         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7823         Store[x][y] = 0;
7824       }
7825     }
7826     else if (element == EL_BD_MAGIC_WALL_FULL)
7827     {
7828       if (IS_FREE(x, y + 1))
7829       {
7830         InitMovingField(x, y, MV_DOWN);
7831         started_moving = TRUE;
7832
7833         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7834         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7835       }
7836       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7837       {
7838         if (!MovDelay[x][y])
7839           MovDelay[x][y] = TILEY/4 + 1;
7840
7841         if (MovDelay[x][y])
7842         {
7843           MovDelay[x][y]--;
7844           if (MovDelay[x][y])
7845             return;
7846         }
7847
7848         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7849         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7850         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7851         Store[x][y] = 0;
7852       }
7853     }
7854     else if (element == EL_DC_MAGIC_WALL_FULL)
7855     {
7856       if (IS_FREE(x, y + 1))
7857       {
7858         InitMovingField(x, y, MV_DOWN);
7859         started_moving = TRUE;
7860
7861         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7862         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7863       }
7864       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7865       {
7866         if (!MovDelay[x][y])
7867           MovDelay[x][y] = TILEY/4 + 1;
7868
7869         if (MovDelay[x][y])
7870         {
7871           MovDelay[x][y]--;
7872           if (MovDelay[x][y])
7873             return;
7874         }
7875
7876         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7877         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7878         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7879         Store[x][y] = 0;
7880       }
7881     }
7882     else if ((CAN_PASS_MAGIC_WALL(element) &&
7883               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7884                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7885              (CAN_PASS_DC_MAGIC_WALL(element) &&
7886               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7887
7888     {
7889       InitMovingField(x, y, MV_DOWN);
7890       started_moving = TRUE;
7891
7892       Feld[x][y] =
7893         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7894          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7895          EL_DC_MAGIC_WALL_FILLING);
7896       Store[x][y] = element;
7897     }
7898     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7899     {
7900       SplashAcid(x, y + 1);
7901
7902       InitMovingField(x, y, MV_DOWN);
7903       started_moving = TRUE;
7904
7905       Store[x][y] = EL_ACID;
7906     }
7907     else if (
7908 #if USE_FIX_IMPACT_COLLISION
7909              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7910               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7911 #else
7912              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7913               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7914 #endif
7915              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7916               CAN_FALL(element) && WasJustFalling[x][y] &&
7917               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7918
7919              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7920               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7921               (Feld[x][y + 1] == EL_BLOCKED)))
7922     {
7923       /* this is needed for a special case not covered by calling "Impact()"
7924          from "ContinueMoving()": if an element moves to a tile directly below
7925          another element which was just falling on that tile (which was empty
7926          in the previous frame), the falling element above would just stop
7927          instead of smashing the element below (in previous version, the above
7928          element was just checked for "moving" instead of "falling", resulting
7929          in incorrect smashes caused by horizontal movement of the above
7930          element; also, the case of the player being the element to smash was
7931          simply not covered here... :-/ ) */
7932
7933       CheckCollision[x][y] = 0;
7934       CheckImpact[x][y] = 0;
7935
7936       Impact(x, y);
7937     }
7938     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7939     {
7940       if (MovDir[x][y] == MV_NONE)
7941       {
7942         InitMovingField(x, y, MV_DOWN);
7943         started_moving = TRUE;
7944       }
7945     }
7946     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7947     {
7948       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7949         MovDir[x][y] = MV_DOWN;
7950
7951       InitMovingField(x, y, MV_DOWN);
7952       started_moving = TRUE;
7953     }
7954     else if (element == EL_AMOEBA_DROP)
7955     {
7956       Feld[x][y] = EL_AMOEBA_GROWING;
7957       Store[x][y] = EL_AMOEBA_WET;
7958     }
7959     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7960               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7961              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7962              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7963     {
7964       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7965                                 (IS_FREE(x - 1, y + 1) ||
7966                                  Feld[x - 1][y + 1] == EL_ACID));
7967       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7968                                 (IS_FREE(x + 1, y + 1) ||
7969                                  Feld[x + 1][y + 1] == EL_ACID));
7970       boolean can_fall_any  = (can_fall_left || can_fall_right);
7971       boolean can_fall_both = (can_fall_left && can_fall_right);
7972       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7973
7974 #if USE_NEW_ALL_SLIPPERY
7975       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7976       {
7977         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7978           can_fall_right = FALSE;
7979         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7980           can_fall_left = FALSE;
7981         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7984           can_fall_left = FALSE;
7985
7986         can_fall_any  = (can_fall_left || can_fall_right);
7987         can_fall_both = FALSE;
7988       }
7989 #else
7990       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7991       {
7992         if (slippery_type == SLIPPERY_ONLY_LEFT)
7993           can_fall_right = FALSE;
7994         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7995           can_fall_left = FALSE;
7996         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7997           can_fall_right = FALSE;
7998         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7999           can_fall_left = FALSE;
8000
8001         can_fall_any  = (can_fall_left || can_fall_right);
8002         can_fall_both = (can_fall_left && can_fall_right);
8003       }
8004 #endif
8005
8006 #if USE_NEW_ALL_SLIPPERY
8007 #else
8008 #if USE_NEW_SP_SLIPPERY
8009       /* !!! better use the same properties as for custom elements here !!! */
8010       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8011                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8012       {
8013         can_fall_right = FALSE;         /* slip down on left side */
8014         can_fall_both = FALSE;
8015       }
8016 #endif
8017 #endif
8018
8019 #if USE_NEW_ALL_SLIPPERY
8020       if (can_fall_both)
8021       {
8022         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8023           can_fall_right = FALSE;       /* slip down on left side */
8024         else
8025           can_fall_left = !(can_fall_right = RND(2));
8026
8027         can_fall_both = FALSE;
8028       }
8029 #else
8030       if (can_fall_both)
8031       {
8032         if (game.emulation == EMU_BOULDERDASH ||
8033             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8034           can_fall_right = FALSE;       /* slip down on left side */
8035         else
8036           can_fall_left = !(can_fall_right = RND(2));
8037
8038         can_fall_both = FALSE;
8039       }
8040 #endif
8041
8042       if (can_fall_any)
8043       {
8044         /* if not determined otherwise, prefer left side for slipping down */
8045         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8046         started_moving = TRUE;
8047       }
8048     }
8049 #if 0
8050     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8051 #else
8052     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8053 #endif
8054     {
8055       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8056       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8057       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8058       int belt_dir = game.belt_dir[belt_nr];
8059
8060       if ((belt_dir == MV_LEFT  && left_is_free) ||
8061           (belt_dir == MV_RIGHT && right_is_free))
8062       {
8063         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8064
8065         InitMovingField(x, y, belt_dir);
8066         started_moving = TRUE;
8067
8068         Pushed[x][y] = TRUE;
8069         Pushed[nextx][y] = TRUE;
8070
8071         GfxAction[x][y] = ACTION_DEFAULT;
8072       }
8073       else
8074       {
8075         MovDir[x][y] = 0;       /* if element was moving, stop it */
8076       }
8077     }
8078   }
8079
8080   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8081 #if 0
8082   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8083 #else
8084   if (CAN_MOVE(element) && !started_moving)
8085 #endif
8086   {
8087     int move_pattern = element_info[element].move_pattern;
8088     int newx, newy;
8089
8090 #if 0
8091 #if DEBUG
8092     if (MovDir[x][y] == MV_NONE)
8093     {
8094       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8095              x, y, element, element_info[element].token_name);
8096       printf("StartMoving(): This should never happen!\n");
8097     }
8098 #endif
8099 #endif
8100
8101     Moving2Blocked(x, y, &newx, &newy);
8102
8103     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8104       return;
8105
8106     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8107         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8108     {
8109       WasJustMoving[x][y] = 0;
8110       CheckCollision[x][y] = 0;
8111
8112       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8113
8114       if (Feld[x][y] != element)        /* element has changed */
8115         return;
8116     }
8117
8118     if (!MovDelay[x][y])        /* start new movement phase */
8119     {
8120       /* all objects that can change their move direction after each step
8121          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8122
8123       if (element != EL_YAMYAM &&
8124           element != EL_DARK_YAMYAM &&
8125           element != EL_PACMAN &&
8126           !(move_pattern & MV_ANY_DIRECTION) &&
8127           move_pattern != MV_TURNING_LEFT &&
8128           move_pattern != MV_TURNING_RIGHT &&
8129           move_pattern != MV_TURNING_LEFT_RIGHT &&
8130           move_pattern != MV_TURNING_RIGHT_LEFT &&
8131           move_pattern != MV_TURNING_RANDOM)
8132       {
8133         TurnRound(x, y);
8134
8135         if (MovDelay[x][y] && (element == EL_BUG ||
8136                                element == EL_SPACESHIP ||
8137                                element == EL_SP_SNIKSNAK ||
8138                                element == EL_SP_ELECTRON ||
8139                                element == EL_MOLE))
8140           TEST_DrawLevelField(x, y);
8141       }
8142     }
8143
8144     if (MovDelay[x][y])         /* wait some time before next movement */
8145     {
8146       MovDelay[x][y]--;
8147
8148       if (element == EL_ROBOT ||
8149           element == EL_YAMYAM ||
8150           element == EL_DARK_YAMYAM)
8151       {
8152         DrawLevelElementAnimationIfNeeded(x, y, element);
8153         PlayLevelSoundAction(x, y, ACTION_WAITING);
8154       }
8155       else if (element == EL_SP_ELECTRON)
8156         DrawLevelElementAnimationIfNeeded(x, y, element);
8157       else if (element == EL_DRAGON)
8158       {
8159         int i;
8160         int dir = MovDir[x][y];
8161         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8162         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8163         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8164                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8165                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8166                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8167         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8168
8169         GfxAction[x][y] = ACTION_ATTACKING;
8170
8171         if (IS_PLAYER(x, y))
8172           DrawPlayerField(x, y);
8173         else
8174           TEST_DrawLevelField(x, y);
8175
8176         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8177
8178         for (i = 1; i <= 3; i++)
8179         {
8180           int xx = x + i * dx;
8181           int yy = y + i * dy;
8182           int sx = SCREENX(xx);
8183           int sy = SCREENY(yy);
8184           int flame_graphic = graphic + (i - 1);
8185
8186           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8187             break;
8188
8189           if (MovDelay[x][y])
8190           {
8191             int flamed = MovingOrBlocked2Element(xx, yy);
8192
8193             /* !!! */
8194 #if 0
8195             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8196               Bang(xx, yy);
8197             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8198               RemoveMovingField(xx, yy);
8199             else
8200               RemoveField(xx, yy);
8201 #else
8202             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8203               Bang(xx, yy);
8204             else
8205               RemoveMovingField(xx, yy);
8206 #endif
8207
8208             ChangeDelay[xx][yy] = 0;
8209
8210             Feld[xx][yy] = EL_FLAMES;
8211
8212             if (IN_SCR_FIELD(sx, sy))
8213             {
8214               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8215               DrawGraphic(sx, sy, flame_graphic, frame);
8216             }
8217           }
8218           else
8219           {
8220             if (Feld[xx][yy] == EL_FLAMES)
8221               Feld[xx][yy] = EL_EMPTY;
8222             TEST_DrawLevelField(xx, yy);
8223           }
8224         }
8225       }
8226
8227       if (MovDelay[x][y])       /* element still has to wait some time */
8228       {
8229         PlayLevelSoundAction(x, y, ACTION_WAITING);
8230
8231         return;
8232       }
8233     }
8234
8235     /* now make next step */
8236
8237     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8238
8239     if (DONT_COLLIDE_WITH(element) &&
8240         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8241         !PLAYER_ENEMY_PROTECTED(newx, newy))
8242     {
8243       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8244
8245       return;
8246     }
8247
8248     else if (CAN_MOVE_INTO_ACID(element) &&
8249              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8250              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8251              (MovDir[x][y] == MV_DOWN ||
8252               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8253     {
8254       SplashAcid(newx, newy);
8255       Store[x][y] = EL_ACID;
8256     }
8257     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8258     {
8259       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8260           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8261           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8262           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8263       {
8264         RemoveField(x, y);
8265         TEST_DrawLevelField(x, y);
8266
8267         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8268         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8269           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8270
8271         local_player->friends_still_needed--;
8272         if (!local_player->friends_still_needed &&
8273             !local_player->GameOver && AllPlayersGone)
8274           PlayerWins(local_player);
8275
8276         return;
8277       }
8278       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8279       {
8280         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8281           TEST_DrawLevelField(newx, newy);
8282         else
8283           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8284       }
8285       else if (!IS_FREE(newx, newy))
8286       {
8287         GfxAction[x][y] = ACTION_WAITING;
8288
8289         if (IS_PLAYER(x, y))
8290           DrawPlayerField(x, y);
8291         else
8292           TEST_DrawLevelField(x, y);
8293
8294         return;
8295       }
8296     }
8297     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8298     {
8299       if (IS_FOOD_PIG(Feld[newx][newy]))
8300       {
8301         if (IS_MOVING(newx, newy))
8302           RemoveMovingField(newx, newy);
8303         else
8304         {
8305           Feld[newx][newy] = EL_EMPTY;
8306           TEST_DrawLevelField(newx, newy);
8307         }
8308
8309         PlayLevelSound(x, y, SND_PIG_DIGGING);
8310       }
8311       else if (!IS_FREE(newx, newy))
8312       {
8313         if (IS_PLAYER(x, y))
8314           DrawPlayerField(x, y);
8315         else
8316           TEST_DrawLevelField(x, y);
8317
8318         return;
8319       }
8320     }
8321     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8322     {
8323       if (Store[x][y] != EL_EMPTY)
8324       {
8325         boolean can_clone = FALSE;
8326         int xx, yy;
8327
8328         /* check if element to clone is still there */
8329         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8330         {
8331           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8332           {
8333             can_clone = TRUE;
8334
8335             break;
8336           }
8337         }
8338
8339         /* cannot clone or target field not free anymore -- do not clone */
8340         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8341           Store[x][y] = EL_EMPTY;
8342       }
8343
8344       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8345       {
8346         if (IS_MV_DIAGONAL(MovDir[x][y]))
8347         {
8348           int diagonal_move_dir = MovDir[x][y];
8349           int stored = Store[x][y];
8350           int change_delay = 8;
8351           int graphic;
8352
8353           /* android is moving diagonally */
8354
8355           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8356
8357           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8358           GfxElement[x][y] = EL_EMC_ANDROID;
8359           GfxAction[x][y] = ACTION_SHRINKING;
8360           GfxDir[x][y] = diagonal_move_dir;
8361           ChangeDelay[x][y] = change_delay;
8362
8363           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8364                                    GfxDir[x][y]);
8365
8366           DrawLevelGraphicAnimation(x, y, graphic);
8367           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8368
8369           if (Feld[newx][newy] == EL_ACID)
8370           {
8371             SplashAcid(newx, newy);
8372
8373             return;
8374           }
8375
8376           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8377
8378           Store[newx][newy] = EL_EMC_ANDROID;
8379           GfxElement[newx][newy] = EL_EMC_ANDROID;
8380           GfxAction[newx][newy] = ACTION_GROWING;
8381           GfxDir[newx][newy] = diagonal_move_dir;
8382           ChangeDelay[newx][newy] = change_delay;
8383
8384           graphic = el_act_dir2img(GfxElement[newx][newy],
8385                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8386
8387           DrawLevelGraphicAnimation(newx, newy, graphic);
8388           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8389
8390           return;
8391         }
8392         else
8393         {
8394           Feld[newx][newy] = EL_EMPTY;
8395           TEST_DrawLevelField(newx, newy);
8396
8397           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8398         }
8399       }
8400       else if (!IS_FREE(newx, newy))
8401       {
8402 #if 0
8403         if (IS_PLAYER(x, y))
8404           DrawPlayerField(x, y);
8405         else
8406           TEST_DrawLevelField(x, y);
8407 #endif
8408
8409         return;
8410       }
8411     }
8412     else if (IS_CUSTOM_ELEMENT(element) &&
8413              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8414     {
8415 #if 1
8416       if (!DigFieldByCE(newx, newy, element))
8417         return;
8418 #else
8419       int new_element = Feld[newx][newy];
8420
8421       if (!IS_FREE(newx, newy))
8422       {
8423         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8424                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8425                       ACTION_BREAKING);
8426
8427         /* no element can dig solid indestructible elements */
8428         if (IS_INDESTRUCTIBLE(new_element) &&
8429             !IS_DIGGABLE(new_element) &&
8430             !IS_COLLECTIBLE(new_element))
8431           return;
8432
8433         if (AmoebaNr[newx][newy] &&
8434             (new_element == EL_AMOEBA_FULL ||
8435              new_element == EL_BD_AMOEBA ||
8436              new_element == EL_AMOEBA_GROWING))
8437         {
8438           AmoebaCnt[AmoebaNr[newx][newy]]--;
8439           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8440         }
8441
8442         if (IS_MOVING(newx, newy))
8443           RemoveMovingField(newx, newy);
8444         else
8445         {
8446           RemoveField(newx, newy);
8447           TEST_DrawLevelField(newx, newy);
8448         }
8449
8450         /* if digged element was about to explode, prevent the explosion */
8451         ExplodeField[newx][newy] = EX_TYPE_NONE;
8452
8453         PlayLevelSoundAction(x, y, action);
8454       }
8455
8456       Store[newx][newy] = EL_EMPTY;
8457
8458 #if 1
8459       /* this makes it possible to leave the removed element again */
8460       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8461         Store[newx][newy] = new_element;
8462 #else
8463       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8464       {
8465         int move_leave_element = element_info[element].move_leave_element;
8466
8467         /* this makes it possible to leave the removed element again */
8468         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8469                              new_element : move_leave_element);
8470       }
8471 #endif
8472
8473 #endif
8474
8475       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8476       {
8477         RunnerVisit[x][y] = FrameCounter;
8478         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8479       }
8480     }
8481     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8482     {
8483       if (!IS_FREE(newx, newy))
8484       {
8485         if (IS_PLAYER(x, y))
8486           DrawPlayerField(x, y);
8487         else
8488           TEST_DrawLevelField(x, y);
8489
8490         return;
8491       }
8492       else
8493       {
8494         boolean wanna_flame = !RND(10);
8495         int dx = newx - x, dy = newy - y;
8496         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8497         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8498         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8499                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8500         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8501                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8502
8503         if ((wanna_flame ||
8504              IS_CLASSIC_ENEMY(element1) ||
8505              IS_CLASSIC_ENEMY(element2)) &&
8506             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8507             element1 != EL_FLAMES && element2 != EL_FLAMES)
8508         {
8509           ResetGfxAnimation(x, y);
8510           GfxAction[x][y] = ACTION_ATTACKING;
8511
8512           if (IS_PLAYER(x, y))
8513             DrawPlayerField(x, y);
8514           else
8515             TEST_DrawLevelField(x, y);
8516
8517           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8518
8519           MovDelay[x][y] = 50;
8520
8521           /* !!! */
8522 #if 0
8523           RemoveField(newx, newy);
8524 #endif
8525           Feld[newx][newy] = EL_FLAMES;
8526           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8527           {
8528 #if 0
8529             RemoveField(newx1, newy1);
8530 #endif
8531             Feld[newx1][newy1] = EL_FLAMES;
8532           }
8533           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8534           {
8535 #if 0
8536             RemoveField(newx2, newy2);
8537 #endif
8538             Feld[newx2][newy2] = EL_FLAMES;
8539           }
8540
8541           return;
8542         }
8543       }
8544     }
8545     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8546              Feld[newx][newy] == EL_DIAMOND)
8547     {
8548       if (IS_MOVING(newx, newy))
8549         RemoveMovingField(newx, newy);
8550       else
8551       {
8552         Feld[newx][newy] = EL_EMPTY;
8553         TEST_DrawLevelField(newx, newy);
8554       }
8555
8556       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8557     }
8558     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8559              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8560     {
8561       if (AmoebaNr[newx][newy])
8562       {
8563         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8564         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8565             Feld[newx][newy] == EL_BD_AMOEBA)
8566           AmoebaCnt[AmoebaNr[newx][newy]]--;
8567       }
8568
8569 #if 0
8570       /* !!! test !!! */
8571       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8572       {
8573         RemoveMovingField(newx, newy);
8574       }
8575 #else
8576       if (IS_MOVING(newx, newy))
8577       {
8578         RemoveMovingField(newx, newy);
8579       }
8580 #endif
8581       else
8582       {
8583         Feld[newx][newy] = EL_EMPTY;
8584         TEST_DrawLevelField(newx, newy);
8585       }
8586
8587       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8588     }
8589     else if ((element == EL_PACMAN || element == EL_MOLE)
8590              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8591     {
8592       if (AmoebaNr[newx][newy])
8593       {
8594         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8595         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8596             Feld[newx][newy] == EL_BD_AMOEBA)
8597           AmoebaCnt[AmoebaNr[newx][newy]]--;
8598       }
8599
8600       if (element == EL_MOLE)
8601       {
8602         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8603         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8604
8605         ResetGfxAnimation(x, y);
8606         GfxAction[x][y] = ACTION_DIGGING;
8607         TEST_DrawLevelField(x, y);
8608
8609         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8610
8611         return;                         /* wait for shrinking amoeba */
8612       }
8613       else      /* element == EL_PACMAN */
8614       {
8615         Feld[newx][newy] = EL_EMPTY;
8616         TEST_DrawLevelField(newx, newy);
8617         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8618       }
8619     }
8620     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8621              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8622               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8623     {
8624       /* wait for shrinking amoeba to completely disappear */
8625       return;
8626     }
8627     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8628     {
8629       /* object was running against a wall */
8630
8631       TurnRound(x, y);
8632
8633 #if 0
8634       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8635       if (move_pattern & MV_ANY_DIRECTION &&
8636           move_pattern == MovDir[x][y])
8637       {
8638         int blocking_element =
8639           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8640
8641         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8642                                  MovDir[x][y]);
8643
8644         element = Feld[x][y];   /* element might have changed */
8645       }
8646 #endif
8647
8648       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8649         DrawLevelElementAnimation(x, y, element);
8650
8651       if (DONT_TOUCH(element))
8652         TestIfBadThingTouchesPlayer(x, y);
8653
8654       return;
8655     }
8656
8657     InitMovingField(x, y, MovDir[x][y]);
8658
8659     PlayLevelSoundAction(x, y, ACTION_MOVING);
8660   }
8661
8662   if (MovDir[x][y])
8663     ContinueMoving(x, y);
8664 }
8665
8666 void ContinueMoving(int x, int y)
8667 {
8668   int element = Feld[x][y];
8669   struct ElementInfo *ei = &element_info[element];
8670   int direction = MovDir[x][y];
8671   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8672   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8673   int newx = x + dx, newy = y + dy;
8674   int stored = Store[x][y];
8675   int stored_new = Store[newx][newy];
8676   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8677   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8678   boolean last_line = (newy == lev_fieldy - 1);
8679
8680   MovPos[x][y] += getElementMoveStepsize(x, y);
8681
8682   if (pushed_by_player) /* special case: moving object pushed by player */
8683     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8684
8685   if (ABS(MovPos[x][y]) < TILEX)
8686   {
8687 #if 0
8688     int ee = Feld[x][y];
8689     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8690     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8691
8692     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8693            x, y, ABS(MovPos[x][y]),
8694            ee, gg, ff,
8695            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8696 #endif
8697
8698     TEST_DrawLevelField(x, y);
8699
8700     return;     /* element is still moving */
8701   }
8702
8703   /* element reached destination field */
8704
8705   Feld[x][y] = EL_EMPTY;
8706   Feld[newx][newy] = element;
8707   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8708
8709   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8710   {
8711     element = Feld[newx][newy] = EL_ACID;
8712   }
8713   else if (element == EL_MOLE)
8714   {
8715     Feld[x][y] = EL_SAND;
8716
8717     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8718   }
8719   else if (element == EL_QUICKSAND_FILLING)
8720   {
8721     element = Feld[newx][newy] = get_next_element(element);
8722     Store[newx][newy] = Store[x][y];
8723   }
8724   else if (element == EL_QUICKSAND_EMPTYING)
8725   {
8726     Feld[x][y] = get_next_element(element);
8727     element = Feld[newx][newy] = Store[x][y];
8728   }
8729   else if (element == EL_QUICKSAND_FAST_FILLING)
8730   {
8731     element = Feld[newx][newy] = get_next_element(element);
8732     Store[newx][newy] = Store[x][y];
8733   }
8734   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8735   {
8736     Feld[x][y] = get_next_element(element);
8737     element = Feld[newx][newy] = Store[x][y];
8738   }
8739   else if (element == EL_MAGIC_WALL_FILLING)
8740   {
8741     element = Feld[newx][newy] = get_next_element(element);
8742     if (!game.magic_wall_active)
8743       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8744     Store[newx][newy] = Store[x][y];
8745   }
8746   else if (element == EL_MAGIC_WALL_EMPTYING)
8747   {
8748     Feld[x][y] = get_next_element(element);
8749     if (!game.magic_wall_active)
8750       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8751     element = Feld[newx][newy] = Store[x][y];
8752
8753 #if USE_NEW_CUSTOM_VALUE
8754     InitField(newx, newy, FALSE);
8755 #endif
8756   }
8757   else if (element == EL_BD_MAGIC_WALL_FILLING)
8758   {
8759     element = Feld[newx][newy] = get_next_element(element);
8760     if (!game.magic_wall_active)
8761       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8762     Store[newx][newy] = Store[x][y];
8763   }
8764   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8765   {
8766     Feld[x][y] = get_next_element(element);
8767     if (!game.magic_wall_active)
8768       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8769     element = Feld[newx][newy] = Store[x][y];
8770
8771 #if USE_NEW_CUSTOM_VALUE
8772     InitField(newx, newy, FALSE);
8773 #endif
8774   }
8775   else if (element == EL_DC_MAGIC_WALL_FILLING)
8776   {
8777     element = Feld[newx][newy] = get_next_element(element);
8778     if (!game.magic_wall_active)
8779       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8780     Store[newx][newy] = Store[x][y];
8781   }
8782   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8783   {
8784     Feld[x][y] = get_next_element(element);
8785     if (!game.magic_wall_active)
8786       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8787     element = Feld[newx][newy] = Store[x][y];
8788
8789 #if USE_NEW_CUSTOM_VALUE
8790     InitField(newx, newy, FALSE);
8791 #endif
8792   }
8793   else if (element == EL_AMOEBA_DROPPING)
8794   {
8795     Feld[x][y] = get_next_element(element);
8796     element = Feld[newx][newy] = Store[x][y];
8797   }
8798   else if (element == EL_SOKOBAN_OBJECT)
8799   {
8800     if (Back[x][y])
8801       Feld[x][y] = Back[x][y];
8802
8803     if (Back[newx][newy])
8804       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8805
8806     Back[x][y] = Back[newx][newy] = 0;
8807   }
8808
8809   Store[x][y] = EL_EMPTY;
8810   MovPos[x][y] = 0;
8811   MovDir[x][y] = 0;
8812   MovDelay[x][y] = 0;
8813
8814   MovDelay[newx][newy] = 0;
8815
8816   if (CAN_CHANGE_OR_HAS_ACTION(element))
8817   {
8818     /* copy element change control values to new field */
8819     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8820     ChangePage[newx][newy]  = ChangePage[x][y];
8821     ChangeCount[newx][newy] = ChangeCount[x][y];
8822     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8823   }
8824
8825 #if USE_NEW_CUSTOM_VALUE
8826   CustomValue[newx][newy] = CustomValue[x][y];
8827 #endif
8828
8829   ChangeDelay[x][y] = 0;
8830   ChangePage[x][y] = -1;
8831   ChangeCount[x][y] = 0;
8832   ChangeEvent[x][y] = -1;
8833
8834 #if USE_NEW_CUSTOM_VALUE
8835   CustomValue[x][y] = 0;
8836 #endif
8837
8838   /* copy animation control values to new field */
8839   GfxFrame[newx][newy]  = GfxFrame[x][y];
8840   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8841   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8842   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8843
8844   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8845
8846   /* some elements can leave other elements behind after moving */
8847 #if 1
8848   if (ei->move_leave_element != EL_EMPTY &&
8849       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8850       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8851 #else
8852   if (IS_CUSTOM_ELEMENT(element) && 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 #endif
8856   {
8857     int move_leave_element = ei->move_leave_element;
8858
8859 #if 1
8860 #if 1
8861     /* this makes it possible to leave the removed element again */
8862     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8863       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8864 #else
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;
8868 #endif
8869 #else
8870     /* this makes it possible to leave the removed element again */
8871     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8872         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8873       move_leave_element = stored;
8874 #endif
8875
8876     Feld[x][y] = move_leave_element;
8877
8878     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8879       MovDir[x][y] = direction;
8880
8881     InitField(x, y, FALSE);
8882
8883     if (GFX_CRUMBLED(Feld[x][y]))
8884       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8885
8886     if (ELEM_IS_PLAYER(move_leave_element))
8887       RelocatePlayer(x, y, move_leave_element);
8888   }
8889
8890   /* do this after checking for left-behind element */
8891   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8892
8893   if (!CAN_MOVE(element) ||
8894       (CAN_FALL(element) && direction == MV_DOWN &&
8895        (element == EL_SPRING ||
8896         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8897         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8898     GfxDir[x][y] = MovDir[newx][newy] = 0;
8899
8900   TEST_DrawLevelField(x, y);
8901   TEST_DrawLevelField(newx, newy);
8902
8903   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8904
8905   /* prevent pushed element from moving on in pushed direction */
8906   if (pushed_by_player && CAN_MOVE(element) &&
8907       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8908       !(element_info[element].move_pattern & direction))
8909     TurnRound(newx, newy);
8910
8911   /* prevent elements on conveyor belt from moving on in last direction */
8912   if (pushed_by_conveyor && CAN_FALL(element) &&
8913       direction & MV_HORIZONTAL)
8914     MovDir[newx][newy] = 0;
8915
8916   if (!pushed_by_player)
8917   {
8918     int nextx = newx + dx, nexty = newy + dy;
8919     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8920
8921     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8922
8923     if (CAN_FALL(element) && direction == MV_DOWN)
8924       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8925
8926     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8927       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8928
8929 #if USE_FIX_IMPACT_COLLISION
8930     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8931       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8932 #endif
8933   }
8934
8935   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8936   {
8937     TestIfBadThingTouchesPlayer(newx, newy);
8938     TestIfBadThingTouchesFriend(newx, newy);
8939
8940     if (!IS_CUSTOM_ELEMENT(element))
8941       TestIfBadThingTouchesOtherBadThing(newx, newy);
8942   }
8943   else if (element == EL_PENGUIN)
8944     TestIfFriendTouchesBadThing(newx, newy);
8945
8946   /* give the player one last chance (one more frame) to move away */
8947   if (CAN_FALL(element) && direction == MV_DOWN &&
8948       (last_line || (!IS_FREE(x, newy + 1) &&
8949                      (!IS_PLAYER(x, newy + 1) ||
8950                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8951     Impact(x, newy);
8952
8953   if (pushed_by_player && !game.use_change_when_pushing_bug)
8954   {
8955     int push_side = MV_DIR_OPPOSITE(direction);
8956     struct PlayerInfo *player = PLAYERINFO(x, y);
8957
8958     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8959                                player->index_bit, push_side);
8960     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8961                                         player->index_bit, push_side);
8962   }
8963
8964   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8965     MovDelay[newx][newy] = 1;
8966
8967   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8968
8969   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8970
8971 #if 0
8972   if (ChangePage[newx][newy] != -1)             /* delayed change */
8973   {
8974     int page = ChangePage[newx][newy];
8975     struct ElementChangeInfo *change = &ei->change_page[page];
8976
8977     ChangePage[newx][newy] = -1;
8978
8979     if (change->can_change)
8980     {
8981       if (ChangeElement(newx, newy, element, page))
8982       {
8983         if (change->post_change_function)
8984           change->post_change_function(newx, newy);
8985       }
8986     }
8987
8988     if (change->has_action)
8989       ExecuteCustomElementAction(newx, newy, element, page);
8990   }
8991 #endif
8992
8993   TestIfElementHitsCustomElement(newx, newy, direction);
8994   TestIfPlayerTouchesCustomElement(newx, newy);
8995   TestIfElementTouchesCustomElement(newx, newy);
8996
8997   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8998       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8999     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9000                              MV_DIR_OPPOSITE(direction));
9001 }
9002
9003 int AmoebeNachbarNr(int ax, int ay)
9004 {
9005   int i;
9006   int element = Feld[ax][ay];
9007   int group_nr = 0;
9008   static int xy[4][2] =
9009   {
9010     { 0, -1 },
9011     { -1, 0 },
9012     { +1, 0 },
9013     { 0, +1 }
9014   };
9015
9016   for (i = 0; i < NUM_DIRECTIONS; i++)
9017   {
9018     int x = ax + xy[i][0];
9019     int y = ay + xy[i][1];
9020
9021     if (!IN_LEV_FIELD(x, y))
9022       continue;
9023
9024     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9025       group_nr = AmoebaNr[x][y];
9026   }
9027
9028   return group_nr;
9029 }
9030
9031 void AmoebenVereinigen(int ax, int ay)
9032 {
9033   int i, x, y, xx, yy;
9034   int new_group_nr = AmoebaNr[ax][ay];
9035   static int xy[4][2] =
9036   {
9037     { 0, -1 },
9038     { -1, 0 },
9039     { +1, 0 },
9040     { 0, +1 }
9041   };
9042
9043   if (new_group_nr == 0)
9044     return;
9045
9046   for (i = 0; i < NUM_DIRECTIONS; i++)
9047   {
9048     x = ax + xy[i][0];
9049     y = ay + xy[i][1];
9050
9051     if (!IN_LEV_FIELD(x, y))
9052       continue;
9053
9054     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9055          Feld[x][y] == EL_BD_AMOEBA ||
9056          Feld[x][y] == EL_AMOEBA_DEAD) &&
9057         AmoebaNr[x][y] != new_group_nr)
9058     {
9059       int old_group_nr = AmoebaNr[x][y];
9060
9061       if (old_group_nr == 0)
9062         return;
9063
9064       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9065       AmoebaCnt[old_group_nr] = 0;
9066       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9067       AmoebaCnt2[old_group_nr] = 0;
9068
9069       SCAN_PLAYFIELD(xx, yy)
9070       {
9071         if (AmoebaNr[xx][yy] == old_group_nr)
9072           AmoebaNr[xx][yy] = new_group_nr;
9073       }
9074     }
9075   }
9076 }
9077
9078 void AmoebeUmwandeln(int ax, int ay)
9079 {
9080   int i, x, y;
9081
9082   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9083   {
9084     int group_nr = AmoebaNr[ax][ay];
9085
9086 #ifdef DEBUG
9087     if (group_nr == 0)
9088     {
9089       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9090       printf("AmoebeUmwandeln(): This should never happen!\n");
9091       return;
9092     }
9093 #endif
9094
9095     SCAN_PLAYFIELD(x, y)
9096     {
9097       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9098       {
9099         AmoebaNr[x][y] = 0;
9100         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9101       }
9102     }
9103
9104     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9105                             SND_AMOEBA_TURNING_TO_GEM :
9106                             SND_AMOEBA_TURNING_TO_ROCK));
9107     Bang(ax, ay);
9108   }
9109   else
9110   {
9111     static int xy[4][2] =
9112     {
9113       { 0, -1 },
9114       { -1, 0 },
9115       { +1, 0 },
9116       { 0, +1 }
9117     };
9118
9119     for (i = 0; i < NUM_DIRECTIONS; i++)
9120     {
9121       x = ax + xy[i][0];
9122       y = ay + xy[i][1];
9123
9124       if (!IN_LEV_FIELD(x, y))
9125         continue;
9126
9127       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9128       {
9129         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9130                               SND_AMOEBA_TURNING_TO_GEM :
9131                               SND_AMOEBA_TURNING_TO_ROCK));
9132         Bang(x, y);
9133       }
9134     }
9135   }
9136 }
9137
9138 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9139 {
9140   int x, y;
9141   int group_nr = AmoebaNr[ax][ay];
9142   boolean done = FALSE;
9143
9144 #ifdef DEBUG
9145   if (group_nr == 0)
9146   {
9147     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9148     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9149     return;
9150   }
9151 #endif
9152
9153   SCAN_PLAYFIELD(x, y)
9154   {
9155     if (AmoebaNr[x][y] == group_nr &&
9156         (Feld[x][y] == EL_AMOEBA_DEAD ||
9157          Feld[x][y] == EL_BD_AMOEBA ||
9158          Feld[x][y] == EL_AMOEBA_GROWING))
9159     {
9160       AmoebaNr[x][y] = 0;
9161       Feld[x][y] = new_element;
9162       InitField(x, y, FALSE);
9163       TEST_DrawLevelField(x, y);
9164       done = TRUE;
9165     }
9166   }
9167
9168   if (done)
9169     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9170                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9171                             SND_BD_AMOEBA_TURNING_TO_GEM));
9172 }
9173
9174 void AmoebeWaechst(int x, int y)
9175 {
9176   static unsigned long sound_delay = 0;
9177   static unsigned long sound_delay_value = 0;
9178
9179   if (!MovDelay[x][y])          /* start new growing cycle */
9180   {
9181     MovDelay[x][y] = 7;
9182
9183     if (DelayReached(&sound_delay, sound_delay_value))
9184     {
9185       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9186       sound_delay_value = 30;
9187     }
9188   }
9189
9190   if (MovDelay[x][y])           /* wait some time before growing bigger */
9191   {
9192     MovDelay[x][y]--;
9193     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9194     {
9195       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9196                                            6 - MovDelay[x][y]);
9197
9198       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9199     }
9200
9201     if (!MovDelay[x][y])
9202     {
9203       Feld[x][y] = Store[x][y];
9204       Store[x][y] = 0;
9205       TEST_DrawLevelField(x, y);
9206     }
9207   }
9208 }
9209
9210 void AmoebaDisappearing(int x, int y)
9211 {
9212   static unsigned long sound_delay = 0;
9213   static unsigned long sound_delay_value = 0;
9214
9215   if (!MovDelay[x][y])          /* start new shrinking cycle */
9216   {
9217     MovDelay[x][y] = 7;
9218
9219     if (DelayReached(&sound_delay, sound_delay_value))
9220       sound_delay_value = 30;
9221   }
9222
9223   if (MovDelay[x][y])           /* wait some time before shrinking */
9224   {
9225     MovDelay[x][y]--;
9226     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9227     {
9228       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9229                                            6 - MovDelay[x][y]);
9230
9231       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9232     }
9233
9234     if (!MovDelay[x][y])
9235     {
9236       Feld[x][y] = EL_EMPTY;
9237       TEST_DrawLevelField(x, y);
9238
9239       /* don't let mole enter this field in this cycle;
9240          (give priority to objects falling to this field from above) */
9241       Stop[x][y] = TRUE;
9242     }
9243   }
9244 }
9245
9246 void AmoebeAbleger(int ax, int ay)
9247 {
9248   int i;
9249   int element = Feld[ax][ay];
9250   int graphic = el2img(element);
9251   int newax = ax, neway = ay;
9252   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9253   static int xy[4][2] =
9254   {
9255     { 0, -1 },
9256     { -1, 0 },
9257     { +1, 0 },
9258     { 0, +1 }
9259   };
9260
9261   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9262   {
9263     Feld[ax][ay] = EL_AMOEBA_DEAD;
9264     TEST_DrawLevelField(ax, ay);
9265     return;
9266   }
9267
9268   if (IS_ANIMATED(graphic))
9269     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9270
9271   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9272     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9273
9274   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9275   {
9276     MovDelay[ax][ay]--;
9277     if (MovDelay[ax][ay])
9278       return;
9279   }
9280
9281   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9282   {
9283     int start = RND(4);
9284     int x = ax + xy[start][0];
9285     int y = ay + xy[start][1];
9286
9287     if (!IN_LEV_FIELD(x, y))
9288       return;
9289
9290     if (IS_FREE(x, y) ||
9291         CAN_GROW_INTO(Feld[x][y]) ||
9292         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9293         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9294     {
9295       newax = x;
9296       neway = y;
9297     }
9298
9299     if (newax == ax && neway == ay)
9300       return;
9301   }
9302   else                          /* normal or "filled" (BD style) amoeba */
9303   {
9304     int start = RND(4);
9305     boolean waiting_for_player = FALSE;
9306
9307     for (i = 0; i < NUM_DIRECTIONS; i++)
9308     {
9309       int j = (start + i) % 4;
9310       int x = ax + xy[j][0];
9311       int y = ay + xy[j][1];
9312
9313       if (!IN_LEV_FIELD(x, y))
9314         continue;
9315
9316       if (IS_FREE(x, y) ||
9317           CAN_GROW_INTO(Feld[x][y]) ||
9318           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9319           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9320       {
9321         newax = x;
9322         neway = y;
9323         break;
9324       }
9325       else if (IS_PLAYER(x, y))
9326         waiting_for_player = TRUE;
9327     }
9328
9329     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9330     {
9331       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9332       {
9333         Feld[ax][ay] = EL_AMOEBA_DEAD;
9334         TEST_DrawLevelField(ax, ay);
9335         AmoebaCnt[AmoebaNr[ax][ay]]--;
9336
9337         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9338         {
9339           if (element == EL_AMOEBA_FULL)
9340             AmoebeUmwandeln(ax, ay);
9341           else if (element == EL_BD_AMOEBA)
9342             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9343         }
9344       }
9345       return;
9346     }
9347     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9348     {
9349       /* amoeba gets larger by growing in some direction */
9350
9351       int new_group_nr = AmoebaNr[ax][ay];
9352
9353 #ifdef DEBUG
9354   if (new_group_nr == 0)
9355   {
9356     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9357     printf("AmoebeAbleger(): This should never happen!\n");
9358     return;
9359   }
9360 #endif
9361
9362       AmoebaNr[newax][neway] = new_group_nr;
9363       AmoebaCnt[new_group_nr]++;
9364       AmoebaCnt2[new_group_nr]++;
9365
9366       /* if amoeba touches other amoeba(s) after growing, unify them */
9367       AmoebenVereinigen(newax, neway);
9368
9369       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9370       {
9371         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9372         return;
9373       }
9374     }
9375   }
9376
9377   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9378       (neway == lev_fieldy - 1 && newax != ax))
9379   {
9380     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9381     Store[newax][neway] = element;
9382   }
9383   else if (neway == ay || element == EL_EMC_DRIPPER)
9384   {
9385     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9386
9387     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9388   }
9389   else
9390   {
9391     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9392     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9393     Store[ax][ay] = EL_AMOEBA_DROP;
9394     ContinueMoving(ax, ay);
9395     return;
9396   }
9397
9398   TEST_DrawLevelField(newax, neway);
9399 }
9400
9401 void Life(int ax, int ay)
9402 {
9403   int x1, y1, x2, y2;
9404   int life_time = 40;
9405   int element = Feld[ax][ay];
9406   int graphic = el2img(element);
9407   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9408                          level.biomaze);
9409   boolean changed = FALSE;
9410
9411   if (IS_ANIMATED(graphic))
9412     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9413
9414   if (Stop[ax][ay])
9415     return;
9416
9417   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9418     MovDelay[ax][ay] = life_time;
9419
9420   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9421   {
9422     MovDelay[ax][ay]--;
9423     if (MovDelay[ax][ay])
9424       return;
9425   }
9426
9427   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9428   {
9429     int xx = ax+x1, yy = ay+y1;
9430     int nachbarn = 0;
9431
9432     if (!IN_LEV_FIELD(xx, yy))
9433       continue;
9434
9435     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9436     {
9437       int x = xx+x2, y = yy+y2;
9438
9439       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9440         continue;
9441
9442       if (((Feld[x][y] == element ||
9443             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9444            !Stop[x][y]) ||
9445           (IS_FREE(x, y) && Stop[x][y]))
9446         nachbarn++;
9447     }
9448
9449     if (xx == ax && yy == ay)           /* field in the middle */
9450     {
9451       if (nachbarn < life_parameter[0] ||
9452           nachbarn > life_parameter[1])
9453       {
9454         Feld[xx][yy] = EL_EMPTY;
9455         if (!Stop[xx][yy])
9456           TEST_DrawLevelField(xx, yy);
9457         Stop[xx][yy] = TRUE;
9458         changed = TRUE;
9459       }
9460     }
9461     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9462     {                                   /* free border field */
9463       if (nachbarn >= life_parameter[2] &&
9464           nachbarn <= life_parameter[3])
9465       {
9466         Feld[xx][yy] = element;
9467         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9468         if (!Stop[xx][yy])
9469           TEST_DrawLevelField(xx, yy);
9470         Stop[xx][yy] = TRUE;
9471         changed = TRUE;
9472       }
9473     }
9474   }
9475
9476   if (changed)
9477     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9478                    SND_GAME_OF_LIFE_GROWING);
9479 }
9480
9481 static void InitRobotWheel(int x, int y)
9482 {
9483   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9484 }
9485
9486 static void RunRobotWheel(int x, int y)
9487 {
9488   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9489 }
9490
9491 static void StopRobotWheel(int x, int y)
9492 {
9493   if (ZX == x && ZY == y)
9494   {
9495     ZX = ZY = -1;
9496
9497     game.robot_wheel_active = FALSE;
9498   }
9499 }
9500
9501 static void InitTimegateWheel(int x, int y)
9502 {
9503   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9504 }
9505
9506 static void RunTimegateWheel(int x, int y)
9507 {
9508   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9509 }
9510
9511 static void InitMagicBallDelay(int x, int y)
9512 {
9513 #if 1
9514   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9515 #else
9516   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9517 #endif
9518 }
9519
9520 static void ActivateMagicBall(int bx, int by)
9521 {
9522   int x, y;
9523
9524   if (level.ball_random)
9525   {
9526     int pos_border = RND(8);    /* select one of the eight border elements */
9527     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9528     int xx = pos_content % 3;
9529     int yy = pos_content / 3;
9530
9531     x = bx - 1 + xx;
9532     y = by - 1 + yy;
9533
9534     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9535       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9536   }
9537   else
9538   {
9539     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9540     {
9541       int xx = x - bx + 1;
9542       int yy = y - by + 1;
9543
9544       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9545         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9546     }
9547   }
9548
9549   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9550 }
9551
9552 void CheckExit(int x, int y)
9553 {
9554   if (local_player->gems_still_needed > 0 ||
9555       local_player->sokobanfields_still_needed > 0 ||
9556       local_player->lights_still_needed > 0)
9557   {
9558     int element = Feld[x][y];
9559     int graphic = el2img(element);
9560
9561     if (IS_ANIMATED(graphic))
9562       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9563
9564     return;
9565   }
9566
9567   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9568     return;
9569
9570   Feld[x][y] = EL_EXIT_OPENING;
9571
9572   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9573 }
9574
9575 void CheckExitEM(int x, int y)
9576 {
9577   if (local_player->gems_still_needed > 0 ||
9578       local_player->sokobanfields_still_needed > 0 ||
9579       local_player->lights_still_needed > 0)
9580   {
9581     int element = Feld[x][y];
9582     int graphic = el2img(element);
9583
9584     if (IS_ANIMATED(graphic))
9585       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9586
9587     return;
9588   }
9589
9590   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9591     return;
9592
9593   Feld[x][y] = EL_EM_EXIT_OPENING;
9594
9595   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9596 }
9597
9598 void CheckExitSteel(int x, int y)
9599 {
9600   if (local_player->gems_still_needed > 0 ||
9601       local_player->sokobanfields_still_needed > 0 ||
9602       local_player->lights_still_needed > 0)
9603   {
9604     int element = Feld[x][y];
9605     int graphic = el2img(element);
9606
9607     if (IS_ANIMATED(graphic))
9608       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9609
9610     return;
9611   }
9612
9613   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9614     return;
9615
9616   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9617
9618   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9619 }
9620
9621 void CheckExitSteelEM(int x, int y)
9622 {
9623   if (local_player->gems_still_needed > 0 ||
9624       local_player->sokobanfields_still_needed > 0 ||
9625       local_player->lights_still_needed > 0)
9626   {
9627     int element = Feld[x][y];
9628     int graphic = el2img(element);
9629
9630     if (IS_ANIMATED(graphic))
9631       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9632
9633     return;
9634   }
9635
9636   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9637     return;
9638
9639   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9640
9641   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9642 }
9643
9644 void CheckExitSP(int x, int y)
9645 {
9646   if (local_player->gems_still_needed > 0)
9647   {
9648     int element = Feld[x][y];
9649     int graphic = el2img(element);
9650
9651     if (IS_ANIMATED(graphic))
9652       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9653
9654     return;
9655   }
9656
9657   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9658     return;
9659
9660   Feld[x][y] = EL_SP_EXIT_OPENING;
9661
9662   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9663 }
9664
9665 static void CloseAllOpenTimegates()
9666 {
9667   int x, y;
9668
9669   SCAN_PLAYFIELD(x, y)
9670   {
9671     int element = Feld[x][y];
9672
9673     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9674     {
9675       Feld[x][y] = EL_TIMEGATE_CLOSING;
9676
9677       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9678     }
9679   }
9680 }
9681
9682 void DrawTwinkleOnField(int x, int y)
9683 {
9684   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9685     return;
9686
9687   if (Feld[x][y] == EL_BD_DIAMOND)
9688     return;
9689
9690   if (MovDelay[x][y] == 0)      /* next animation frame */
9691     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9692
9693   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9694   {
9695     MovDelay[x][y]--;
9696
9697     DrawLevelElementAnimation(x, y, Feld[x][y]);
9698
9699     if (MovDelay[x][y] != 0)
9700     {
9701       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9702                                            10 - MovDelay[x][y]);
9703
9704       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9705     }
9706   }
9707 }
9708
9709 void MauerWaechst(int x, int y)
9710 {
9711   int delay = 6;
9712
9713   if (!MovDelay[x][y])          /* next animation frame */
9714     MovDelay[x][y] = 3 * delay;
9715
9716   if (MovDelay[x][y])           /* wait some time before next frame */
9717   {
9718     MovDelay[x][y]--;
9719
9720     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9721     {
9722       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9723       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9724
9725       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9726     }
9727
9728     if (!MovDelay[x][y])
9729     {
9730       if (MovDir[x][y] == MV_LEFT)
9731       {
9732         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9733           TEST_DrawLevelField(x - 1, y);
9734       }
9735       else if (MovDir[x][y] == MV_RIGHT)
9736       {
9737         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9738           TEST_DrawLevelField(x + 1, y);
9739       }
9740       else if (MovDir[x][y] == MV_UP)
9741       {
9742         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9743           TEST_DrawLevelField(x, y - 1);
9744       }
9745       else
9746       {
9747         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9748           TEST_DrawLevelField(x, y + 1);
9749       }
9750
9751       Feld[x][y] = Store[x][y];
9752       Store[x][y] = 0;
9753       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9754       TEST_DrawLevelField(x, y);
9755     }
9756   }
9757 }
9758
9759 void MauerAbleger(int ax, int ay)
9760 {
9761   int element = Feld[ax][ay];
9762   int graphic = el2img(element);
9763   boolean oben_frei = FALSE, unten_frei = FALSE;
9764   boolean links_frei = FALSE, rechts_frei = FALSE;
9765   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9766   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9767   boolean new_wall = FALSE;
9768
9769   if (IS_ANIMATED(graphic))
9770     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9771
9772   if (!MovDelay[ax][ay])        /* start building new wall */
9773     MovDelay[ax][ay] = 6;
9774
9775   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9776   {
9777     MovDelay[ax][ay]--;
9778     if (MovDelay[ax][ay])
9779       return;
9780   }
9781
9782   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9783     oben_frei = TRUE;
9784   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9785     unten_frei = TRUE;
9786   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9787     links_frei = TRUE;
9788   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9789     rechts_frei = TRUE;
9790
9791   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9792       element == EL_EXPANDABLE_WALL_ANY)
9793   {
9794     if (oben_frei)
9795     {
9796       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9797       Store[ax][ay-1] = element;
9798       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9799       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9800         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9801                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9802       new_wall = TRUE;
9803     }
9804     if (unten_frei)
9805     {
9806       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9807       Store[ax][ay+1] = element;
9808       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9809       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9810         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9811                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9812       new_wall = TRUE;
9813     }
9814   }
9815
9816   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9817       element == EL_EXPANDABLE_WALL_ANY ||
9818       element == EL_EXPANDABLE_WALL ||
9819       element == EL_BD_EXPANDABLE_WALL)
9820   {
9821     if (links_frei)
9822     {
9823       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9824       Store[ax-1][ay] = element;
9825       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9826       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9827         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9828                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9829       new_wall = TRUE;
9830     }
9831
9832     if (rechts_frei)
9833     {
9834       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9835       Store[ax+1][ay] = element;
9836       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9837       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9838         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9839                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9840       new_wall = TRUE;
9841     }
9842   }
9843
9844   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9845     TEST_DrawLevelField(ax, ay);
9846
9847   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9848     oben_massiv = TRUE;
9849   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9850     unten_massiv = TRUE;
9851   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9852     links_massiv = TRUE;
9853   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9854     rechts_massiv = TRUE;
9855
9856   if (((oben_massiv && unten_massiv) ||
9857        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9858        element == EL_EXPANDABLE_WALL) &&
9859       ((links_massiv && rechts_massiv) ||
9860        element == EL_EXPANDABLE_WALL_VERTICAL))
9861     Feld[ax][ay] = EL_WALL;
9862
9863   if (new_wall)
9864     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9865 }
9866
9867 void MauerAblegerStahl(int ax, int ay)
9868 {
9869   int element = Feld[ax][ay];
9870   int graphic = el2img(element);
9871   boolean oben_frei = FALSE, unten_frei = FALSE;
9872   boolean links_frei = FALSE, rechts_frei = FALSE;
9873   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9874   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9875   boolean new_wall = FALSE;
9876
9877   if (IS_ANIMATED(graphic))
9878     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9879
9880   if (!MovDelay[ax][ay])        /* start building new wall */
9881     MovDelay[ax][ay] = 6;
9882
9883   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9884   {
9885     MovDelay[ax][ay]--;
9886     if (MovDelay[ax][ay])
9887       return;
9888   }
9889
9890   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9891     oben_frei = TRUE;
9892   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9893     unten_frei = TRUE;
9894   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9895     links_frei = TRUE;
9896   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9897     rechts_frei = TRUE;
9898
9899   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9900       element == EL_EXPANDABLE_STEELWALL_ANY)
9901   {
9902     if (oben_frei)
9903     {
9904       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9905       Store[ax][ay-1] = element;
9906       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9907       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9908         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9909                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9910       new_wall = TRUE;
9911     }
9912     if (unten_frei)
9913     {
9914       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9915       Store[ax][ay+1] = element;
9916       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9917       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9918         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9919                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9920       new_wall = TRUE;
9921     }
9922   }
9923
9924   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9925       element == EL_EXPANDABLE_STEELWALL_ANY)
9926   {
9927     if (links_frei)
9928     {
9929       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9930       Store[ax-1][ay] = element;
9931       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9932       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9933         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9934                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9935       new_wall = TRUE;
9936     }
9937
9938     if (rechts_frei)
9939     {
9940       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9941       Store[ax+1][ay] = element;
9942       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9943       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9944         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9945                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9946       new_wall = TRUE;
9947     }
9948   }
9949
9950   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9951     oben_massiv = TRUE;
9952   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9953     unten_massiv = TRUE;
9954   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9955     links_massiv = TRUE;
9956   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9957     rechts_massiv = TRUE;
9958
9959   if (((oben_massiv && unten_massiv) ||
9960        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9961       ((links_massiv && rechts_massiv) ||
9962        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9963     Feld[ax][ay] = EL_STEELWALL;
9964
9965   if (new_wall)
9966     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9967 }
9968
9969 void CheckForDragon(int x, int y)
9970 {
9971   int i, j;
9972   boolean dragon_found = FALSE;
9973   static int xy[4][2] =
9974   {
9975     { 0, -1 },
9976     { -1, 0 },
9977     { +1, 0 },
9978     { 0, +1 }
9979   };
9980
9981   for (i = 0; i < NUM_DIRECTIONS; i++)
9982   {
9983     for (j = 0; j < 4; j++)
9984     {
9985       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9986
9987       if (IN_LEV_FIELD(xx, yy) &&
9988           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9989       {
9990         if (Feld[xx][yy] == EL_DRAGON)
9991           dragon_found = TRUE;
9992       }
9993       else
9994         break;
9995     }
9996   }
9997
9998   if (!dragon_found)
9999   {
10000     for (i = 0; i < NUM_DIRECTIONS; i++)
10001     {
10002       for (j = 0; j < 3; j++)
10003       {
10004         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10005   
10006         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10007         {
10008           Feld[xx][yy] = EL_EMPTY;
10009           TEST_DrawLevelField(xx, yy);
10010         }
10011         else
10012           break;
10013       }
10014     }
10015   }
10016 }
10017
10018 static void InitBuggyBase(int x, int y)
10019 {
10020   int element = Feld[x][y];
10021   int activating_delay = FRAMES_PER_SECOND / 4;
10022
10023   ChangeDelay[x][y] =
10024     (element == EL_SP_BUGGY_BASE ?
10025      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10026      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10027      activating_delay :
10028      element == EL_SP_BUGGY_BASE_ACTIVE ?
10029      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10030 }
10031
10032 static void WarnBuggyBase(int x, int y)
10033 {
10034   int i;
10035   static int xy[4][2] =
10036   {
10037     { 0, -1 },
10038     { -1, 0 },
10039     { +1, 0 },
10040     { 0, +1 }
10041   };
10042
10043   for (i = 0; i < NUM_DIRECTIONS; i++)
10044   {
10045     int xx = x + xy[i][0];
10046     int yy = y + xy[i][1];
10047
10048     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10049     {
10050       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10051
10052       break;
10053     }
10054   }
10055 }
10056
10057 static void InitTrap(int x, int y)
10058 {
10059   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10060 }
10061
10062 static void ActivateTrap(int x, int y)
10063 {
10064   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10065 }
10066
10067 static void ChangeActiveTrap(int x, int y)
10068 {
10069   int graphic = IMG_TRAP_ACTIVE;
10070
10071   /* if new animation frame was drawn, correct crumbled sand border */
10072   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10073     TEST_DrawLevelFieldCrumbledSand(x, y);
10074 }
10075
10076 static int getSpecialActionElement(int element, int number, int base_element)
10077 {
10078   return (element != EL_EMPTY ? element :
10079           number != -1 ? base_element + number - 1 :
10080           EL_EMPTY);
10081 }
10082
10083 static int getModifiedActionNumber(int value_old, int operator, int operand,
10084                                    int value_min, int value_max)
10085 {
10086   int value_new = (operator == CA_MODE_SET      ? operand :
10087                    operator == CA_MODE_ADD      ? value_old + operand :
10088                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10089                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10090                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10091                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10092                    value_old);
10093
10094   return (value_new < value_min ? value_min :
10095           value_new > value_max ? value_max :
10096           value_new);
10097 }
10098
10099 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10100 {
10101   struct ElementInfo *ei = &element_info[element];
10102   struct ElementChangeInfo *change = &ei->change_page[page];
10103   int target_element = change->target_element;
10104   int action_type = change->action_type;
10105   int action_mode = change->action_mode;
10106   int action_arg = change->action_arg;
10107   int i;
10108
10109   if (!change->has_action)
10110     return;
10111
10112   /* ---------- determine action paramater values -------------------------- */
10113
10114   int level_time_value =
10115     (level.time > 0 ? TimeLeft :
10116      TimePlayed);
10117
10118   int action_arg_element =
10119     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10120      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10121      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10122      EL_EMPTY);
10123
10124   int action_arg_direction =
10125     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10126      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10127      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10128      change->actual_trigger_side :
10129      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10130      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10131      MV_NONE);
10132
10133   int action_arg_number_min =
10134     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10135      CA_ARG_MIN);
10136
10137   int action_arg_number_max =
10138     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10139      action_type == CA_SET_LEVEL_GEMS ? 999 :
10140      action_type == CA_SET_LEVEL_TIME ? 9999 :
10141      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10142      action_type == CA_SET_CE_VALUE ? 9999 :
10143      action_type == CA_SET_CE_SCORE ? 9999 :
10144      CA_ARG_MAX);
10145
10146   int action_arg_number_reset =
10147     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10148      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10149      action_type == CA_SET_LEVEL_TIME ? level.time :
10150      action_type == CA_SET_LEVEL_SCORE ? 0 :
10151 #if USE_NEW_CUSTOM_VALUE
10152      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10153 #else
10154      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10155 #endif
10156      action_type == CA_SET_CE_SCORE ? 0 :
10157      0);
10158
10159   int action_arg_number =
10160     (action_arg <= CA_ARG_MAX ? action_arg :
10161      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10162      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10163      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10164      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10165      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10166      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10167 #if USE_NEW_CUSTOM_VALUE
10168      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10169 #else
10170      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10171 #endif
10172      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10173      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10174      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10175      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10176      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10177      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10178      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10179      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10180      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10181      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10182      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10183      -1);
10184
10185   int action_arg_number_old =
10186     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10187      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10188      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10189      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10190      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10191      0);
10192
10193   int action_arg_number_new =
10194     getModifiedActionNumber(action_arg_number_old,
10195                             action_mode, action_arg_number,
10196                             action_arg_number_min, action_arg_number_max);
10197
10198 #if 1
10199   int trigger_player_bits = change->actual_trigger_player_bits;
10200 #else
10201   int trigger_player_bits =
10202     (change->actual_trigger_player >= EL_PLAYER_1 &&
10203      change->actual_trigger_player <= EL_PLAYER_4 ?
10204      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10205      PLAYER_BITS_ANY);
10206 #endif
10207
10208   int action_arg_player_bits =
10209     (action_arg >= CA_ARG_PLAYER_1 &&
10210      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10211      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10212      PLAYER_BITS_ANY);
10213
10214   /* ---------- execute action  -------------------------------------------- */
10215
10216   switch (action_type)
10217   {
10218     case CA_NO_ACTION:
10219     {
10220       return;
10221     }
10222
10223     /* ---------- level actions  ------------------------------------------- */
10224
10225     case CA_RESTART_LEVEL:
10226     {
10227       game.restart_level = TRUE;
10228
10229       break;
10230     }
10231
10232     case CA_SHOW_ENVELOPE:
10233     {
10234       int element = getSpecialActionElement(action_arg_element,
10235                                             action_arg_number, EL_ENVELOPE_1);
10236
10237       if (IS_ENVELOPE(element))
10238         local_player->show_envelope = element;
10239
10240       break;
10241     }
10242
10243     case CA_SET_LEVEL_TIME:
10244     {
10245       if (level.time > 0)       /* only modify limited time value */
10246       {
10247         TimeLeft = action_arg_number_new;
10248
10249 #if 1
10250         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10251
10252         DisplayGameControlValues();
10253 #else
10254         DrawGameValue_Time(TimeLeft);
10255 #endif
10256
10257         if (!TimeLeft && setup.time_limit)
10258           for (i = 0; i < MAX_PLAYERS; i++)
10259             KillPlayer(&stored_player[i]);
10260       }
10261
10262       break;
10263     }
10264
10265     case CA_SET_LEVEL_SCORE:
10266     {
10267       local_player->score = action_arg_number_new;
10268
10269 #if 1
10270       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10271
10272       DisplayGameControlValues();
10273 #else
10274       DrawGameValue_Score(local_player->score);
10275 #endif
10276
10277       break;
10278     }
10279
10280     case CA_SET_LEVEL_GEMS:
10281     {
10282       local_player->gems_still_needed = action_arg_number_new;
10283
10284 #if 1
10285       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10286
10287       DisplayGameControlValues();
10288 #else
10289       DrawGameValue_Emeralds(local_player->gems_still_needed);
10290 #endif
10291
10292       break;
10293     }
10294
10295 #if !USE_PLAYER_GRAVITY
10296     case CA_SET_LEVEL_GRAVITY:
10297     {
10298       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10299                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10300                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10301                       game.gravity);
10302       break;
10303     }
10304 #endif
10305
10306     case CA_SET_LEVEL_WIND:
10307     {
10308       game.wind_direction = action_arg_direction;
10309
10310       break;
10311     }
10312
10313     /* ---------- player actions  ------------------------------------------ */
10314
10315     case CA_MOVE_PLAYER:
10316     {
10317       /* automatically move to the next field in specified direction */
10318       for (i = 0; i < MAX_PLAYERS; i++)
10319         if (trigger_player_bits & (1 << i))
10320           stored_player[i].programmed_action = action_arg_direction;
10321
10322       break;
10323     }
10324
10325     case CA_EXIT_PLAYER:
10326     {
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328         if (action_arg_player_bits & (1 << i))
10329           PlayerWins(&stored_player[i]);
10330
10331       break;
10332     }
10333
10334     case CA_KILL_PLAYER:
10335     {
10336       for (i = 0; i < MAX_PLAYERS; i++)
10337         if (action_arg_player_bits & (1 << i))
10338           KillPlayer(&stored_player[i]);
10339
10340       break;
10341     }
10342
10343     case CA_SET_PLAYER_KEYS:
10344     {
10345       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10346       int element = getSpecialActionElement(action_arg_element,
10347                                             action_arg_number, EL_KEY_1);
10348
10349       if (IS_KEY(element))
10350       {
10351         for (i = 0; i < MAX_PLAYERS; i++)
10352         {
10353           if (trigger_player_bits & (1 << i))
10354           {
10355             stored_player[i].key[KEY_NR(element)] = key_state;
10356
10357             DrawGameDoorValues();
10358           }
10359         }
10360       }
10361
10362       break;
10363     }
10364
10365     case CA_SET_PLAYER_SPEED:
10366     {
10367       for (i = 0; i < MAX_PLAYERS; i++)
10368       {
10369         if (trigger_player_bits & (1 << i))
10370         {
10371           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10372
10373           if (action_arg == CA_ARG_SPEED_FASTER &&
10374               stored_player[i].cannot_move)
10375           {
10376             action_arg_number = STEPSIZE_VERY_SLOW;
10377           }
10378           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10379                    action_arg == CA_ARG_SPEED_FASTER)
10380           {
10381             action_arg_number = 2;
10382             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10383                            CA_MODE_MULTIPLY);
10384           }
10385           else if (action_arg == CA_ARG_NUMBER_RESET)
10386           {
10387             action_arg_number = level.initial_player_stepsize[i];
10388           }
10389
10390           move_stepsize =
10391             getModifiedActionNumber(move_stepsize,
10392                                     action_mode,
10393                                     action_arg_number,
10394                                     action_arg_number_min,
10395                                     action_arg_number_max);
10396
10397           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10398         }
10399       }
10400
10401       break;
10402     }
10403
10404     case CA_SET_PLAYER_SHIELD:
10405     {
10406       for (i = 0; i < MAX_PLAYERS; i++)
10407       {
10408         if (trigger_player_bits & (1 << i))
10409         {
10410           if (action_arg == CA_ARG_SHIELD_OFF)
10411           {
10412             stored_player[i].shield_normal_time_left = 0;
10413             stored_player[i].shield_deadly_time_left = 0;
10414           }
10415           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10416           {
10417             stored_player[i].shield_normal_time_left = 999999;
10418           }
10419           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10420           {
10421             stored_player[i].shield_normal_time_left = 999999;
10422             stored_player[i].shield_deadly_time_left = 999999;
10423           }
10424         }
10425       }
10426
10427       break;
10428     }
10429
10430 #if USE_PLAYER_GRAVITY
10431     case CA_SET_PLAYER_GRAVITY:
10432     {
10433       for (i = 0; i < MAX_PLAYERS; i++)
10434       {
10435         if (trigger_player_bits & (1 << i))
10436         {
10437           stored_player[i].gravity =
10438             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10439              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10440              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10441              stored_player[i].gravity);
10442         }
10443       }
10444
10445       break;
10446     }
10447 #endif
10448
10449     case CA_SET_PLAYER_ARTWORK:
10450     {
10451       for (i = 0; i < MAX_PLAYERS; i++)
10452       {
10453         if (trigger_player_bits & (1 << i))
10454         {
10455           int artwork_element = action_arg_element;
10456
10457           if (action_arg == CA_ARG_ELEMENT_RESET)
10458             artwork_element =
10459               (level.use_artwork_element[i] ? level.artwork_element[i] :
10460                stored_player[i].element_nr);
10461
10462 #if USE_GFX_RESET_PLAYER_ARTWORK
10463           if (stored_player[i].artwork_element != artwork_element)
10464             stored_player[i].Frame = 0;
10465 #endif
10466
10467           stored_player[i].artwork_element = artwork_element;
10468
10469           SetPlayerWaiting(&stored_player[i], FALSE);
10470
10471           /* set number of special actions for bored and sleeping animation */
10472           stored_player[i].num_special_action_bored =
10473             get_num_special_action(artwork_element,
10474                                    ACTION_BORING_1, ACTION_BORING_LAST);
10475           stored_player[i].num_special_action_sleeping =
10476             get_num_special_action(artwork_element,
10477                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10478         }
10479       }
10480
10481       break;
10482     }
10483
10484     /* ---------- CE actions  ---------------------------------------------- */
10485
10486     case CA_SET_CE_VALUE:
10487     {
10488 #if USE_NEW_CUSTOM_VALUE
10489       int last_ce_value = CustomValue[x][y];
10490
10491       CustomValue[x][y] = action_arg_number_new;
10492
10493       if (CustomValue[x][y] != last_ce_value)
10494       {
10495         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10496         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10497
10498         if (CustomValue[x][y] == 0)
10499         {
10500           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10501           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10502         }
10503       }
10504 #endif
10505
10506       break;
10507     }
10508
10509     case CA_SET_CE_SCORE:
10510     {
10511 #if USE_NEW_CUSTOM_VALUE
10512       int last_ce_score = ei->collect_score;
10513
10514       ei->collect_score = action_arg_number_new;
10515
10516       if (ei->collect_score != last_ce_score)
10517       {
10518         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10519         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10520
10521         if (ei->collect_score == 0)
10522         {
10523           int xx, yy;
10524
10525           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10526           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10527
10528           /*
10529             This is a very special case that seems to be a mixture between
10530             CheckElementChange() and CheckTriggeredElementChange(): while
10531             the first one only affects single elements that are triggered
10532             directly, the second one affects multiple elements in the playfield
10533             that are triggered indirectly by another element. This is a third
10534             case: Changing the CE score always affects multiple identical CEs,
10535             so every affected CE must be checked, not only the single CE for
10536             which the CE score was changed in the first place (as every instance
10537             of that CE shares the same CE score, and therefore also can change)!
10538           */
10539           SCAN_PLAYFIELD(xx, yy)
10540           {
10541             if (Feld[xx][yy] == element)
10542               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10543                                  CE_SCORE_GETS_ZERO);
10544           }
10545         }
10546       }
10547 #endif
10548
10549       break;
10550     }
10551
10552     /* ---------- engine actions  ------------------------------------------ */
10553
10554     case CA_SET_ENGINE_SCAN_MODE:
10555     {
10556       InitPlayfieldScanMode(action_arg);
10557
10558       break;
10559     }
10560
10561     default:
10562       break;
10563   }
10564 }
10565
10566 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10567 {
10568   int old_element = Feld[x][y];
10569   int new_element = GetElementFromGroupElement(element);
10570   int previous_move_direction = MovDir[x][y];
10571 #if USE_NEW_CUSTOM_VALUE
10572   int last_ce_value = CustomValue[x][y];
10573 #endif
10574   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10575   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10576   boolean add_player_onto_element = (new_element_is_player &&
10577 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10578                                      /* this breaks SnakeBite when a snake is
10579                                         halfway through a door that closes */
10580                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10581                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10582 #endif
10583                                      IS_WALKABLE(old_element));
10584
10585 #if 0
10586   /* check if element under the player changes from accessible to unaccessible
10587      (needed for special case of dropping element which then changes) */
10588   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10589       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10590   {
10591     Bang(x, y);
10592
10593     return;
10594   }
10595 #endif
10596
10597   if (!add_player_onto_element)
10598   {
10599     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10600       RemoveMovingField(x, y);
10601     else
10602       RemoveField(x, y);
10603
10604     Feld[x][y] = new_element;
10605
10606 #if !USE_GFX_RESET_GFX_ANIMATION
10607     ResetGfxAnimation(x, y);
10608     ResetRandomAnimationValue(x, y);
10609 #endif
10610
10611     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10612       MovDir[x][y] = previous_move_direction;
10613
10614 #if USE_NEW_CUSTOM_VALUE
10615     if (element_info[new_element].use_last_ce_value)
10616       CustomValue[x][y] = last_ce_value;
10617 #endif
10618
10619     InitField_WithBug1(x, y, FALSE);
10620
10621     new_element = Feld[x][y];   /* element may have changed */
10622
10623 #if USE_GFX_RESET_GFX_ANIMATION
10624     ResetGfxAnimation(x, y);
10625     ResetRandomAnimationValue(x, y);
10626 #endif
10627
10628     TEST_DrawLevelField(x, y);
10629
10630     if (GFX_CRUMBLED(new_element))
10631       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10632   }
10633
10634 #if 1
10635   /* check if element under the player changes from accessible to unaccessible
10636      (needed for special case of dropping element which then changes) */
10637   /* (must be checked after creating new element for walkable group elements) */
10638 #if USE_FIX_KILLED_BY_NON_WALKABLE
10639   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10640       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10641   {
10642     Bang(x, y);
10643
10644     return;
10645   }
10646 #else
10647   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10648       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10649   {
10650     Bang(x, y);
10651
10652     return;
10653   }
10654 #endif
10655 #endif
10656
10657   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10658   if (new_element_is_player)
10659     RelocatePlayer(x, y, new_element);
10660
10661   if (is_change)
10662     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10663
10664   TestIfBadThingTouchesPlayer(x, y);
10665   TestIfPlayerTouchesCustomElement(x, y);
10666   TestIfElementTouchesCustomElement(x, y);
10667 }
10668
10669 static void CreateField(int x, int y, int element)
10670 {
10671   CreateFieldExt(x, y, element, FALSE);
10672 }
10673
10674 static void CreateElementFromChange(int x, int y, int element)
10675 {
10676   element = GET_VALID_RUNTIME_ELEMENT(element);
10677
10678 #if USE_STOP_CHANGED_ELEMENTS
10679   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10680   {
10681     int old_element = Feld[x][y];
10682
10683     /* prevent changed element from moving in same engine frame
10684        unless both old and new element can either fall or move */
10685     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10686         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10687       Stop[x][y] = TRUE;
10688   }
10689 #endif
10690
10691   CreateFieldExt(x, y, element, TRUE);
10692 }
10693
10694 static boolean ChangeElement(int x, int y, int element, int page)
10695 {
10696   struct ElementInfo *ei = &element_info[element];
10697   struct ElementChangeInfo *change = &ei->change_page[page];
10698   int ce_value = CustomValue[x][y];
10699   int ce_score = ei->collect_score;
10700   int target_element;
10701   int old_element = Feld[x][y];
10702
10703   /* always use default change event to prevent running into a loop */
10704   if (ChangeEvent[x][y] == -1)
10705     ChangeEvent[x][y] = CE_DELAY;
10706
10707   if (ChangeEvent[x][y] == CE_DELAY)
10708   {
10709     /* reset actual trigger element, trigger player and action element */
10710     change->actual_trigger_element = EL_EMPTY;
10711     change->actual_trigger_player = EL_PLAYER_1;
10712     change->actual_trigger_player_bits = CH_PLAYER_1;
10713     change->actual_trigger_side = CH_SIDE_NONE;
10714     change->actual_trigger_ce_value = 0;
10715     change->actual_trigger_ce_score = 0;
10716   }
10717
10718   /* do not change elements more than a specified maximum number of changes */
10719   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10720     return FALSE;
10721
10722   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10723
10724   if (change->explode)
10725   {
10726     Bang(x, y);
10727
10728     return TRUE;
10729   }
10730
10731   if (change->use_target_content)
10732   {
10733     boolean complete_replace = TRUE;
10734     boolean can_replace[3][3];
10735     int xx, yy;
10736
10737     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10738     {
10739       boolean is_empty;
10740       boolean is_walkable;
10741       boolean is_diggable;
10742       boolean is_collectible;
10743       boolean is_removable;
10744       boolean is_destructible;
10745       int ex = x + xx - 1;
10746       int ey = y + yy - 1;
10747       int content_element = change->target_content.e[xx][yy];
10748       int e;
10749
10750       can_replace[xx][yy] = TRUE;
10751
10752       if (ex == x && ey == y)   /* do not check changing element itself */
10753         continue;
10754
10755       if (content_element == EL_EMPTY_SPACE)
10756       {
10757         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10758
10759         continue;
10760       }
10761
10762       if (!IN_LEV_FIELD(ex, ey))
10763       {
10764         can_replace[xx][yy] = FALSE;
10765         complete_replace = FALSE;
10766
10767         continue;
10768       }
10769
10770       e = Feld[ex][ey];
10771
10772       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773         e = MovingOrBlocked2Element(ex, ey);
10774
10775       is_empty = (IS_FREE(ex, ey) ||
10776                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10777
10778       is_walkable     = (is_empty || IS_WALKABLE(e));
10779       is_diggable     = (is_empty || IS_DIGGABLE(e));
10780       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10781       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10782       is_removable    = (is_diggable || is_collectible);
10783
10784       can_replace[xx][yy] =
10785         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10786           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10787           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10788           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10789           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10790           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10791          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10792
10793       if (!can_replace[xx][yy])
10794         complete_replace = FALSE;
10795     }
10796
10797     if (!change->only_if_complete || complete_replace)
10798     {
10799       boolean something_has_changed = FALSE;
10800
10801       if (change->only_if_complete && change->use_random_replace &&
10802           RND(100) < change->random_percentage)
10803         return FALSE;
10804
10805       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10806       {
10807         int ex = x + xx - 1;
10808         int ey = y + yy - 1;
10809         int content_element;
10810
10811         if (can_replace[xx][yy] && (!change->use_random_replace ||
10812                                     RND(100) < change->random_percentage))
10813         {
10814           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10815             RemoveMovingField(ex, ey);
10816
10817           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10818
10819           content_element = change->target_content.e[xx][yy];
10820           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10821                                               ce_value, ce_score);
10822
10823           CreateElementFromChange(ex, ey, target_element);
10824
10825           something_has_changed = TRUE;
10826
10827           /* for symmetry reasons, freeze newly created border elements */
10828           if (ex != x || ey != y)
10829             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10830         }
10831       }
10832
10833       if (something_has_changed)
10834       {
10835         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10836         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10837       }
10838     }
10839   }
10840   else
10841   {
10842     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10843                                         ce_value, ce_score);
10844
10845     if (element == EL_DIAGONAL_GROWING ||
10846         element == EL_DIAGONAL_SHRINKING)
10847     {
10848       target_element = Store[x][y];
10849
10850       Store[x][y] = EL_EMPTY;
10851     }
10852
10853     CreateElementFromChange(x, y, target_element);
10854
10855     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10856     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10857   }
10858
10859   /* this uses direct change before indirect change */
10860   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10861
10862   return TRUE;
10863 }
10864
10865 #if USE_NEW_DELAYED_ACTION
10866
10867 static void HandleElementChange(int x, int y, int page)
10868 {
10869   int element = MovingOrBlocked2Element(x, y);
10870   struct ElementInfo *ei = &element_info[element];
10871   struct ElementChangeInfo *change = &ei->change_page[page];
10872
10873 #ifdef DEBUG
10874   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10875       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10876   {
10877     printf("\n\n");
10878     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10879            x, y, element, element_info[element].token_name);
10880     printf("HandleElementChange(): This should never happen!\n");
10881     printf("\n\n");
10882   }
10883 #endif
10884
10885   /* this can happen with classic bombs on walkable, changing elements */
10886   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10887   {
10888 #if 0
10889     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10890       ChangeDelay[x][y] = 0;
10891 #endif
10892
10893     return;
10894   }
10895
10896   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10897   {
10898     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10899
10900     if (change->can_change)
10901     {
10902 #if 1
10903       /* !!! not clear why graphic animation should be reset at all here !!! */
10904       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10905 #if USE_GFX_RESET_WHEN_NOT_MOVING
10906       /* when a custom element is about to change (for example by change delay),
10907          do not reset graphic animation when the custom element is moving */
10908       if (!IS_MOVING(x, y))
10909 #endif
10910       {
10911         ResetGfxAnimation(x, y);
10912         ResetRandomAnimationValue(x, y);
10913       }
10914 #endif
10915
10916       if (change->pre_change_function)
10917         change->pre_change_function(x, y);
10918     }
10919   }
10920
10921   ChangeDelay[x][y]--;
10922
10923   if (ChangeDelay[x][y] != 0)           /* continue element change */
10924   {
10925     if (change->can_change)
10926     {
10927       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10928
10929       if (IS_ANIMATED(graphic))
10930         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10931
10932       if (change->change_function)
10933         change->change_function(x, y);
10934     }
10935   }
10936   else                                  /* finish element change */
10937   {
10938     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10939     {
10940       page = ChangePage[x][y];
10941       ChangePage[x][y] = -1;
10942
10943       change = &ei->change_page[page];
10944     }
10945
10946     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10947     {
10948       ChangeDelay[x][y] = 1;            /* try change after next move step */
10949       ChangePage[x][y] = page;          /* remember page to use for change */
10950
10951       return;
10952     }
10953
10954     if (change->can_change)
10955     {
10956       if (ChangeElement(x, y, element, page))
10957       {
10958         if (change->post_change_function)
10959           change->post_change_function(x, y);
10960       }
10961     }
10962
10963     if (change->has_action)
10964       ExecuteCustomElementAction(x, y, element, page);
10965   }
10966 }
10967
10968 #else
10969
10970 static void HandleElementChange(int x, int y, int page)
10971 {
10972   int element = MovingOrBlocked2Element(x, y);
10973   struct ElementInfo *ei = &element_info[element];
10974   struct ElementChangeInfo *change = &ei->change_page[page];
10975
10976 #ifdef DEBUG
10977   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10978   {
10979     printf("\n\n");
10980     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10981            x, y, element, element_info[element].token_name);
10982     printf("HandleElementChange(): This should never happen!\n");
10983     printf("\n\n");
10984   }
10985 #endif
10986
10987   /* this can happen with classic bombs on walkable, changing elements */
10988   if (!CAN_CHANGE(element))
10989   {
10990 #if 0
10991     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10992       ChangeDelay[x][y] = 0;
10993 #endif
10994
10995     return;
10996   }
10997
10998   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10999   {
11000     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11001
11002     ResetGfxAnimation(x, y);
11003     ResetRandomAnimationValue(x, y);
11004
11005     if (change->pre_change_function)
11006       change->pre_change_function(x, y);
11007   }
11008
11009   ChangeDelay[x][y]--;
11010
11011   if (ChangeDelay[x][y] != 0)           /* continue element change */
11012   {
11013     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11014
11015     if (IS_ANIMATED(graphic))
11016       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11017
11018     if (change->change_function)
11019       change->change_function(x, y);
11020   }
11021   else                                  /* finish element change */
11022   {
11023     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11024     {
11025       page = ChangePage[x][y];
11026       ChangePage[x][y] = -1;
11027
11028       change = &ei->change_page[page];
11029     }
11030
11031     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11032     {
11033       ChangeDelay[x][y] = 1;            /* try change after next move step */
11034       ChangePage[x][y] = page;          /* remember page to use for change */
11035
11036       return;
11037     }
11038
11039     if (ChangeElement(x, y, element, page))
11040     {
11041       if (change->post_change_function)
11042         change->post_change_function(x, y);
11043     }
11044   }
11045 }
11046
11047 #endif
11048
11049 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11050                                               int trigger_element,
11051                                               int trigger_event,
11052                                               int trigger_player,
11053                                               int trigger_side,
11054                                               int trigger_page)
11055 {
11056   boolean change_done_any = FALSE;
11057   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11058   int i;
11059
11060   if (!(trigger_events[trigger_element][trigger_event]))
11061     return FALSE;
11062
11063 #if 0
11064   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11065          trigger_event, recursion_loop_depth, recursion_loop_detected,
11066          recursion_loop_element, EL_NAME(recursion_loop_element));
11067 #endif
11068
11069   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11070
11071   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11072   {
11073     int element = EL_CUSTOM_START + i;
11074     boolean change_done = FALSE;
11075     int p;
11076
11077     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11078         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11079       continue;
11080
11081     for (p = 0; p < element_info[element].num_change_pages; p++)
11082     {
11083       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11084
11085       if (change->can_change_or_has_action &&
11086           change->has_event[trigger_event] &&
11087           change->trigger_side & trigger_side &&
11088           change->trigger_player & trigger_player &&
11089           change->trigger_page & trigger_page_bits &&
11090           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11091       {
11092         change->actual_trigger_element = trigger_element;
11093         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11094         change->actual_trigger_player_bits = trigger_player;
11095         change->actual_trigger_side = trigger_side;
11096         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11097         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11098
11099         if ((change->can_change && !change_done) || change->has_action)
11100         {
11101           int x, y;
11102
11103           SCAN_PLAYFIELD(x, y)
11104           {
11105             if (Feld[x][y] == element)
11106             {
11107               if (change->can_change && !change_done)
11108               {
11109                 ChangeDelay[x][y] = 1;
11110                 ChangeEvent[x][y] = trigger_event;
11111
11112                 HandleElementChange(x, y, p);
11113               }
11114 #if USE_NEW_DELAYED_ACTION
11115               else if (change->has_action)
11116               {
11117                 ExecuteCustomElementAction(x, y, element, p);
11118                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11119               }
11120 #else
11121               if (change->has_action)
11122               {
11123                 ExecuteCustomElementAction(x, y, element, p);
11124                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11125               }
11126 #endif
11127             }
11128           }
11129
11130           if (change->can_change)
11131           {
11132             change_done = TRUE;
11133             change_done_any = TRUE;
11134           }
11135         }
11136       }
11137     }
11138   }
11139
11140   RECURSION_LOOP_DETECTION_END();
11141
11142   return change_done_any;
11143 }
11144
11145 static boolean CheckElementChangeExt(int x, int y,
11146                                      int element,
11147                                      int trigger_element,
11148                                      int trigger_event,
11149                                      int trigger_player,
11150                                      int trigger_side)
11151 {
11152   boolean change_done = FALSE;
11153   int p;
11154
11155   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11156       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11157     return FALSE;
11158
11159   if (Feld[x][y] == EL_BLOCKED)
11160   {
11161     Blocked2Moving(x, y, &x, &y);
11162     element = Feld[x][y];
11163   }
11164
11165 #if 0
11166   /* check if element has already changed */
11167   if (Feld[x][y] != element)
11168     return FALSE;
11169 #else
11170   /* check if element has already changed or is about to change after moving */
11171   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11172        Feld[x][y] != element) ||
11173
11174       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11175        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11176         ChangePage[x][y] != -1)))
11177     return FALSE;
11178 #endif
11179
11180 #if 0
11181   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11182          trigger_event, recursion_loop_depth, recursion_loop_detected,
11183          recursion_loop_element, EL_NAME(recursion_loop_element));
11184 #endif
11185
11186   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11187
11188   for (p = 0; p < element_info[element].num_change_pages; p++)
11189   {
11190     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11191
11192     /* check trigger element for all events where the element that is checked
11193        for changing interacts with a directly adjacent element -- this is
11194        different to element changes that affect other elements to change on the
11195        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11196     boolean check_trigger_element =
11197       (trigger_event == CE_TOUCHING_X ||
11198        trigger_event == CE_HITTING_X ||
11199        trigger_event == CE_HIT_BY_X ||
11200 #if 1
11201        /* this one was forgotten until 3.2.3 */
11202        trigger_event == CE_DIGGING_X);
11203 #endif
11204
11205     if (change->can_change_or_has_action &&
11206         change->has_event[trigger_event] &&
11207         change->trigger_side & trigger_side &&
11208         change->trigger_player & trigger_player &&
11209         (!check_trigger_element ||
11210          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11211     {
11212       change->actual_trigger_element = trigger_element;
11213       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11214       change->actual_trigger_player_bits = trigger_player;
11215       change->actual_trigger_side = trigger_side;
11216       change->actual_trigger_ce_value = CustomValue[x][y];
11217       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11218
11219       /* special case: trigger element not at (x,y) position for some events */
11220       if (check_trigger_element)
11221       {
11222         static struct
11223         {
11224           int dx, dy;
11225         } move_xy[] =
11226           {
11227             {  0,  0 },
11228             { -1,  0 },
11229             { +1,  0 },
11230             {  0,  0 },
11231             {  0, -1 },
11232             {  0,  0 }, { 0, 0 }, { 0, 0 },
11233             {  0, +1 }
11234           };
11235
11236         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11237         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11238
11239         change->actual_trigger_ce_value = CustomValue[xx][yy];
11240         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11241       }
11242
11243       if (change->can_change && !change_done)
11244       {
11245         ChangeDelay[x][y] = 1;
11246         ChangeEvent[x][y] = trigger_event;
11247
11248         HandleElementChange(x, y, p);
11249
11250         change_done = TRUE;
11251       }
11252 #if USE_NEW_DELAYED_ACTION
11253       else if (change->has_action)
11254       {
11255         ExecuteCustomElementAction(x, y, element, p);
11256         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11257       }
11258 #else
11259       if (change->has_action)
11260       {
11261         ExecuteCustomElementAction(x, y, element, p);
11262         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11263       }
11264 #endif
11265     }
11266   }
11267
11268   RECURSION_LOOP_DETECTION_END();
11269
11270   return change_done;
11271 }
11272
11273 static void PlayPlayerSound(struct PlayerInfo *player)
11274 {
11275   int jx = player->jx, jy = player->jy;
11276   int sound_element = player->artwork_element;
11277   int last_action = player->last_action_waiting;
11278   int action = player->action_waiting;
11279
11280   if (player->is_waiting)
11281   {
11282     if (action != last_action)
11283       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11284     else
11285       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11286   }
11287   else
11288   {
11289     if (action != last_action)
11290       StopSound(element_info[sound_element].sound[last_action]);
11291
11292     if (last_action == ACTION_SLEEPING)
11293       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11294   }
11295 }
11296
11297 static void PlayAllPlayersSound()
11298 {
11299   int i;
11300
11301   for (i = 0; i < MAX_PLAYERS; i++)
11302     if (stored_player[i].active)
11303       PlayPlayerSound(&stored_player[i]);
11304 }
11305
11306 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11307 {
11308   boolean last_waiting = player->is_waiting;
11309   int move_dir = player->MovDir;
11310
11311   player->dir_waiting = move_dir;
11312   player->last_action_waiting = player->action_waiting;
11313
11314   if (is_waiting)
11315   {
11316     if (!last_waiting)          /* not waiting -> waiting */
11317     {
11318       player->is_waiting = TRUE;
11319
11320       player->frame_counter_bored =
11321         FrameCounter +
11322         game.player_boring_delay_fixed +
11323         GetSimpleRandom(game.player_boring_delay_random);
11324       player->frame_counter_sleeping =
11325         FrameCounter +
11326         game.player_sleeping_delay_fixed +
11327         GetSimpleRandom(game.player_sleeping_delay_random);
11328
11329       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11330     }
11331
11332     if (game.player_sleeping_delay_fixed +
11333         game.player_sleeping_delay_random > 0 &&
11334         player->anim_delay_counter == 0 &&
11335         player->post_delay_counter == 0 &&
11336         FrameCounter >= player->frame_counter_sleeping)
11337       player->is_sleeping = TRUE;
11338     else if (game.player_boring_delay_fixed +
11339              game.player_boring_delay_random > 0 &&
11340              FrameCounter >= player->frame_counter_bored)
11341       player->is_bored = TRUE;
11342
11343     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11344                               player->is_bored ? ACTION_BORING :
11345                               ACTION_WAITING);
11346
11347     if (player->is_sleeping && player->use_murphy)
11348     {
11349       /* special case for sleeping Murphy when leaning against non-free tile */
11350
11351       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11352           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11353            !IS_MOVING(player->jx - 1, player->jy)))
11354         move_dir = MV_LEFT;
11355       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11356                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11357                 !IS_MOVING(player->jx + 1, player->jy)))
11358         move_dir = MV_RIGHT;
11359       else
11360         player->is_sleeping = FALSE;
11361
11362       player->dir_waiting = move_dir;
11363     }
11364
11365     if (player->is_sleeping)
11366     {
11367       if (player->num_special_action_sleeping > 0)
11368       {
11369         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11370         {
11371           int last_special_action = player->special_action_sleeping;
11372           int num_special_action = player->num_special_action_sleeping;
11373           int special_action =
11374             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11375              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11376              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11377              last_special_action + 1 : ACTION_SLEEPING);
11378           int special_graphic =
11379             el_act_dir2img(player->artwork_element, special_action, move_dir);
11380
11381           player->anim_delay_counter =
11382             graphic_info[special_graphic].anim_delay_fixed +
11383             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11384           player->post_delay_counter =
11385             graphic_info[special_graphic].post_delay_fixed +
11386             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11387
11388           player->special_action_sleeping = special_action;
11389         }
11390
11391         if (player->anim_delay_counter > 0)
11392         {
11393           player->action_waiting = player->special_action_sleeping;
11394           player->anim_delay_counter--;
11395         }
11396         else if (player->post_delay_counter > 0)
11397         {
11398           player->post_delay_counter--;
11399         }
11400       }
11401     }
11402     else if (player->is_bored)
11403     {
11404       if (player->num_special_action_bored > 0)
11405       {
11406         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11407         {
11408           int special_action =
11409             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11410           int special_graphic =
11411             el_act_dir2img(player->artwork_element, special_action, move_dir);
11412
11413           player->anim_delay_counter =
11414             graphic_info[special_graphic].anim_delay_fixed +
11415             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11416           player->post_delay_counter =
11417             graphic_info[special_graphic].post_delay_fixed +
11418             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11419
11420           player->special_action_bored = special_action;
11421         }
11422
11423         if (player->anim_delay_counter > 0)
11424         {
11425           player->action_waiting = player->special_action_bored;
11426           player->anim_delay_counter--;
11427         }
11428         else if (player->post_delay_counter > 0)
11429         {
11430           player->post_delay_counter--;
11431         }
11432       }
11433     }
11434   }
11435   else if (last_waiting)        /* waiting -> not waiting */
11436   {
11437     player->is_waiting = FALSE;
11438     player->is_bored = FALSE;
11439     player->is_sleeping = FALSE;
11440
11441     player->frame_counter_bored = -1;
11442     player->frame_counter_sleeping = -1;
11443
11444     player->anim_delay_counter = 0;
11445     player->post_delay_counter = 0;
11446
11447     player->dir_waiting = player->MovDir;
11448     player->action_waiting = ACTION_DEFAULT;
11449
11450     player->special_action_bored = ACTION_DEFAULT;
11451     player->special_action_sleeping = ACTION_DEFAULT;
11452   }
11453 }
11454
11455 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11456 {
11457   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11458   int left      = player_action & JOY_LEFT;
11459   int right     = player_action & JOY_RIGHT;
11460   int up        = player_action & JOY_UP;
11461   int down      = player_action & JOY_DOWN;
11462   int button1   = player_action & JOY_BUTTON_1;
11463   int button2   = player_action & JOY_BUTTON_2;
11464   int dx        = (left ? -1 : right ? 1 : 0);
11465   int dy        = (up   ? -1 : down  ? 1 : 0);
11466
11467   if (!player->active || tape.pausing)
11468     return 0;
11469
11470   if (player_action)
11471   {
11472     if (button1)
11473       snapped = SnapField(player, dx, dy);
11474     else
11475     {
11476       if (button2)
11477         dropped = DropElement(player);
11478
11479       moved = MovePlayer(player, dx, dy);
11480     }
11481
11482     if (tape.single_step && tape.recording && !tape.pausing)
11483     {
11484       if (button1 || (dropped && !moved))
11485       {
11486         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11487         SnapField(player, 0, 0);                /* stop snapping */
11488       }
11489     }
11490
11491     SetPlayerWaiting(player, FALSE);
11492
11493     return player_action;
11494   }
11495   else
11496   {
11497     /* no actions for this player (no input at player's configured device) */
11498
11499     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11500     SnapField(player, 0, 0);
11501     CheckGravityMovementWhenNotMoving(player);
11502
11503     if (player->MovPos == 0)
11504       SetPlayerWaiting(player, TRUE);
11505
11506     if (player->MovPos == 0)    /* needed for tape.playing */
11507       player->is_moving = FALSE;
11508
11509     player->is_dropping = FALSE;
11510     player->is_dropping_pressed = FALSE;
11511     player->drop_pressed_delay = 0;
11512
11513     return 0;
11514   }
11515 }
11516
11517 static void CheckLevelTime()
11518 {
11519   int i;
11520
11521   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11522   {
11523     if (level.native_em_level->lev->home == 0)  /* all players at home */
11524     {
11525       PlayerWins(local_player);
11526
11527       AllPlayersGone = TRUE;
11528
11529       level.native_em_level->lev->home = -1;
11530     }
11531
11532     if (level.native_em_level->ply[0]->alive == 0 &&
11533         level.native_em_level->ply[1]->alive == 0 &&
11534         level.native_em_level->ply[2]->alive == 0 &&
11535         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11536       AllPlayersGone = TRUE;
11537   }
11538
11539   if (TimeFrames >= FRAMES_PER_SECOND)
11540   {
11541     TimeFrames = 0;
11542     TapeTime++;
11543
11544     for (i = 0; i < MAX_PLAYERS; i++)
11545     {
11546       struct PlayerInfo *player = &stored_player[i];
11547
11548       if (SHIELD_ON(player))
11549       {
11550         player->shield_normal_time_left--;
11551
11552         if (player->shield_deadly_time_left > 0)
11553           player->shield_deadly_time_left--;
11554       }
11555     }
11556
11557     if (!local_player->LevelSolved && !level.use_step_counter)
11558     {
11559       TimePlayed++;
11560
11561       if (TimeLeft > 0)
11562       {
11563         TimeLeft--;
11564
11565         if (TimeLeft <= 10 && setup.time_limit)
11566           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11567
11568 #if 1
11569         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11570
11571         DisplayGameControlValues();
11572 #else
11573         DrawGameValue_Time(TimeLeft);
11574 #endif
11575
11576         if (!TimeLeft && setup.time_limit)
11577         {
11578           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11579             level.native_em_level->lev->killed_out_of_time = TRUE;
11580           else
11581             for (i = 0; i < MAX_PLAYERS; i++)
11582               KillPlayer(&stored_player[i]);
11583         }
11584       }
11585 #if 1
11586       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11587       {
11588         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11589
11590         DisplayGameControlValues();
11591       }
11592 #else
11593       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11594         DrawGameValue_Time(TimePlayed);
11595 #endif
11596
11597       level.native_em_level->lev->time =
11598         (level.time == 0 ? TimePlayed : TimeLeft);
11599     }
11600
11601     if (tape.recording || tape.playing)
11602       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11603   }
11604
11605 #if 1
11606   UpdateAndDisplayGameControlValues();
11607 #else
11608   UpdateGameDoorValues();
11609   DrawGameDoorValues();
11610 #endif
11611 }
11612
11613 void AdvanceFrameAndPlayerCounters(int player_nr)
11614 {
11615   int i;
11616
11617   /* advance frame counters (global frame counter and time frame counter) */
11618   FrameCounter++;
11619   TimeFrames++;
11620
11621   /* advance player counters (counters for move delay, move animation etc.) */
11622   for (i = 0; i < MAX_PLAYERS; i++)
11623   {
11624     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11625     int move_delay_value = stored_player[i].move_delay_value;
11626     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11627
11628     if (!advance_player_counters)       /* not all players may be affected */
11629       continue;
11630
11631 #if USE_NEW_PLAYER_ANIM
11632     if (move_frames == 0)       /* less than one move per game frame */
11633     {
11634       int stepsize = TILEX / move_delay_value;
11635       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11636       int count = (stored_player[i].is_moving ?
11637                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11638
11639       if (count % delay == 0)
11640         move_frames = 1;
11641     }
11642 #endif
11643
11644     stored_player[i].Frame += move_frames;
11645
11646     if (stored_player[i].MovPos != 0)
11647       stored_player[i].StepFrame += move_frames;
11648
11649     if (stored_player[i].move_delay > 0)
11650       stored_player[i].move_delay--;
11651
11652     /* due to bugs in previous versions, counter must count up, not down */
11653     if (stored_player[i].push_delay != -1)
11654       stored_player[i].push_delay++;
11655
11656     if (stored_player[i].drop_delay > 0)
11657       stored_player[i].drop_delay--;
11658
11659     if (stored_player[i].is_dropping_pressed)
11660       stored_player[i].drop_pressed_delay++;
11661   }
11662 }
11663
11664 void StartGameActions(boolean init_network_game, boolean record_tape,
11665                       long random_seed)
11666 {
11667   unsigned long new_random_seed = InitRND(random_seed);
11668
11669   if (record_tape)
11670     TapeStartRecording(new_random_seed);
11671
11672 #if defined(NETWORK_AVALIABLE)
11673   if (init_network_game)
11674   {
11675     SendToServer_StartPlaying();
11676
11677     return;
11678   }
11679 #endif
11680
11681   InitGame();
11682 }
11683
11684 void GameActions()
11685 {
11686   static unsigned long game_frame_delay = 0;
11687   unsigned long game_frame_delay_value;
11688   byte *recorded_player_action;
11689   byte summarized_player_action = 0;
11690   byte tape_action[MAX_PLAYERS];
11691   int i;
11692
11693   /* detect endless loops, caused by custom element programming */
11694   if (recursion_loop_detected && recursion_loop_depth == 0)
11695   {
11696     char *message = getStringCat3("Internal Error ! Element ",
11697                                   EL_NAME(recursion_loop_element),
11698                                   " caused endless loop ! Quit the game ?");
11699
11700     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11701           EL_NAME(recursion_loop_element));
11702
11703     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11704
11705     recursion_loop_detected = FALSE;    /* if game should be continued */
11706
11707     free(message);
11708
11709     return;
11710   }
11711
11712   if (game.restart_level)
11713     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11714
11715   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11716   {
11717     if (level.native_em_level->lev->home == 0)  /* all players at home */
11718     {
11719       PlayerWins(local_player);
11720
11721       AllPlayersGone = TRUE;
11722
11723       level.native_em_level->lev->home = -1;
11724     }
11725
11726     if (level.native_em_level->ply[0]->alive == 0 &&
11727         level.native_em_level->ply[1]->alive == 0 &&
11728         level.native_em_level->ply[2]->alive == 0 &&
11729         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11730       AllPlayersGone = TRUE;
11731   }
11732
11733   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11734     GameWon();
11735
11736   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11737     TapeStop();
11738
11739   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11740     return;
11741
11742   game_frame_delay_value =
11743     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11744
11745   if (tape.playing && tape.warp_forward && !tape.pausing)
11746     game_frame_delay_value = 0;
11747
11748   /* ---------- main game synchronization point ---------- */
11749
11750   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11751
11752   if (network_playing && !network_player_action_received)
11753   {
11754     /* try to get network player actions in time */
11755
11756 #if defined(NETWORK_AVALIABLE)
11757     /* last chance to get network player actions without main loop delay */
11758     HandleNetworking();
11759 #endif
11760
11761     /* game was quit by network peer */
11762     if (game_status != GAME_MODE_PLAYING)
11763       return;
11764
11765     if (!network_player_action_received)
11766       return;           /* failed to get network player actions in time */
11767
11768     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11769   }
11770
11771   if (tape.pausing)
11772     return;
11773
11774   /* at this point we know that we really continue executing the game */
11775
11776   network_player_action_received = FALSE;
11777
11778   /* when playing tape, read previously recorded player input from tape data */
11779   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11780
11781 #if 1
11782   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11783   if (tape.pausing)
11784     return;
11785 #endif
11786
11787   if (tape.set_centered_player)
11788   {
11789     game.centered_player_nr_next = tape.centered_player_nr_next;
11790     game.set_centered_player = TRUE;
11791   }
11792
11793   for (i = 0; i < MAX_PLAYERS; i++)
11794   {
11795     summarized_player_action |= stored_player[i].action;
11796
11797     if (!network_playing)
11798       stored_player[i].effective_action = stored_player[i].action;
11799   }
11800
11801 #if defined(NETWORK_AVALIABLE)
11802   if (network_playing)
11803     SendToServer_MovePlayer(summarized_player_action);
11804 #endif
11805
11806   if (!options.network && !setup.team_mode)
11807     local_player->effective_action = summarized_player_action;
11808
11809   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11810   {
11811     for (i = 0; i < MAX_PLAYERS; i++)
11812       stored_player[i].effective_action =
11813         (i == game.centered_player_nr ? summarized_player_action : 0);
11814   }
11815
11816   if (recorded_player_action != NULL)
11817     for (i = 0; i < MAX_PLAYERS; i++)
11818       stored_player[i].effective_action = recorded_player_action[i];
11819
11820   for (i = 0; i < MAX_PLAYERS; i++)
11821   {
11822     tape_action[i] = stored_player[i].effective_action;
11823
11824     /* (this can only happen in the R'n'D game engine) */
11825     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11826       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11827   }
11828
11829   /* only record actions from input devices, but not programmed actions */
11830   if (tape.recording)
11831     TapeRecordAction(tape_action);
11832
11833   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11834   {
11835     GameActions_EM_Main();
11836   }
11837   else
11838   {
11839     GameActions_RND();
11840   }
11841 }
11842
11843 void GameActions_EM_Main()
11844 {
11845   byte effective_action[MAX_PLAYERS];
11846   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11847   int i;
11848
11849   for (i = 0; i < MAX_PLAYERS; i++)
11850     effective_action[i] = stored_player[i].effective_action;
11851
11852   GameActions_EM(effective_action, warp_mode);
11853
11854   CheckLevelTime();
11855
11856   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11857 }
11858
11859 void GameActions_RND()
11860 {
11861   int magic_wall_x = 0, magic_wall_y = 0;
11862   int i, x, y, element, graphic;
11863
11864   InitPlayfieldScanModeVars();
11865
11866 #if USE_ONE_MORE_CHANGE_PER_FRAME
11867   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11868   {
11869     SCAN_PLAYFIELD(x, y)
11870     {
11871       ChangeCount[x][y] = 0;
11872       ChangeEvent[x][y] = -1;
11873     }
11874   }
11875 #endif
11876
11877   if (game.set_centered_player)
11878   {
11879     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11880
11881     /* switching to "all players" only possible if all players fit to screen */
11882     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11883     {
11884       game.centered_player_nr_next = game.centered_player_nr;
11885       game.set_centered_player = FALSE;
11886     }
11887
11888     /* do not switch focus to non-existing (or non-active) player */
11889     if (game.centered_player_nr_next >= 0 &&
11890         !stored_player[game.centered_player_nr_next].active)
11891     {
11892       game.centered_player_nr_next = game.centered_player_nr;
11893       game.set_centered_player = FALSE;
11894     }
11895   }
11896
11897   if (game.set_centered_player &&
11898       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11899   {
11900     int sx, sy;
11901
11902     if (game.centered_player_nr_next == -1)
11903     {
11904       setScreenCenteredToAllPlayers(&sx, &sy);
11905     }
11906     else
11907     {
11908       sx = stored_player[game.centered_player_nr_next].jx;
11909       sy = stored_player[game.centered_player_nr_next].jy;
11910     }
11911
11912     game.centered_player_nr = game.centered_player_nr_next;
11913     game.set_centered_player = FALSE;
11914
11915     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11916     DrawGameDoorValues();
11917   }
11918
11919   for (i = 0; i < MAX_PLAYERS; i++)
11920   {
11921     int actual_player_action = stored_player[i].effective_action;
11922
11923 #if 1
11924     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11925        - rnd_equinox_tetrachloride 048
11926        - rnd_equinox_tetrachloride_ii 096
11927        - rnd_emanuel_schmieg 002
11928        - doctor_sloan_ww 001, 020
11929     */
11930     if (stored_player[i].MovPos == 0)
11931       CheckGravityMovement(&stored_player[i]);
11932 #endif
11933
11934     /* overwrite programmed action with tape action */
11935     if (stored_player[i].programmed_action)
11936       actual_player_action = stored_player[i].programmed_action;
11937
11938     PlayerActions(&stored_player[i], actual_player_action);
11939
11940     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11941   }
11942
11943   ScrollScreen(NULL, SCROLL_GO_ON);
11944
11945   /* for backwards compatibility, the following code emulates a fixed bug that
11946      occured when pushing elements (causing elements that just made their last
11947      pushing step to already (if possible) make their first falling step in the
11948      same game frame, which is bad); this code is also needed to use the famous
11949      "spring push bug" which is used in older levels and might be wanted to be
11950      used also in newer levels, but in this case the buggy pushing code is only
11951      affecting the "spring" element and no other elements */
11952
11953   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11954   {
11955     for (i = 0; i < MAX_PLAYERS; i++)
11956     {
11957       struct PlayerInfo *player = &stored_player[i];
11958       int x = player->jx;
11959       int y = player->jy;
11960
11961       if (player->active && player->is_pushing && player->is_moving &&
11962           IS_MOVING(x, y) &&
11963           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11964            Feld[x][y] == EL_SPRING))
11965       {
11966         ContinueMoving(x, y);
11967
11968         /* continue moving after pushing (this is actually a bug) */
11969         if (!IS_MOVING(x, y))
11970           Stop[x][y] = FALSE;
11971       }
11972     }
11973   }
11974
11975 #if 0
11976   debug_print_timestamp(0, "start main loop profiling");
11977 #endif
11978
11979   SCAN_PLAYFIELD(x, y)
11980   {
11981     ChangeCount[x][y] = 0;
11982     ChangeEvent[x][y] = -1;
11983
11984     /* this must be handled before main playfield loop */
11985     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11986     {
11987       MovDelay[x][y]--;
11988       if (MovDelay[x][y] <= 0)
11989         RemoveField(x, y);
11990     }
11991
11992 #if USE_NEW_SNAP_DELAY
11993     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11994     {
11995       MovDelay[x][y]--;
11996       if (MovDelay[x][y] <= 0)
11997       {
11998         RemoveField(x, y);
11999         TEST_DrawLevelField(x, y);
12000
12001         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12002       }
12003     }
12004 #endif
12005
12006 #if DEBUG
12007     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12008     {
12009       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12010       printf("GameActions(): This should never happen!\n");
12011
12012       ChangePage[x][y] = -1;
12013     }
12014 #endif
12015
12016     Stop[x][y] = FALSE;
12017     if (WasJustMoving[x][y] > 0)
12018       WasJustMoving[x][y]--;
12019     if (WasJustFalling[x][y] > 0)
12020       WasJustFalling[x][y]--;
12021     if (CheckCollision[x][y] > 0)
12022       CheckCollision[x][y]--;
12023     if (CheckImpact[x][y] > 0)
12024       CheckImpact[x][y]--;
12025
12026     GfxFrame[x][y]++;
12027
12028     /* reset finished pushing action (not done in ContinueMoving() to allow
12029        continuous pushing animation for elements with zero push delay) */
12030     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12031     {
12032       ResetGfxAnimation(x, y);
12033       TEST_DrawLevelField(x, y);
12034     }
12035
12036 #if DEBUG
12037     if (IS_BLOCKED(x, y))
12038     {
12039       int oldx, oldy;
12040
12041       Blocked2Moving(x, y, &oldx, &oldy);
12042       if (!IS_MOVING(oldx, oldy))
12043       {
12044         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12045         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12046         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12047         printf("GameActions(): This should never happen!\n");
12048       }
12049     }
12050 #endif
12051   }
12052
12053 #if 0
12054   debug_print_timestamp(0, "- time for pre-main loop:");
12055 #endif
12056
12057 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12058   SCAN_PLAYFIELD(x, y)
12059   {
12060     element = Feld[x][y];
12061     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12062
12063 #if 1
12064     {
12065 #if 1
12066       int element2 = element;
12067       int graphic2 = graphic;
12068 #else
12069       int element2 = Feld[x][y];
12070       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12071 #endif
12072       int last_gfx_frame = GfxFrame[x][y];
12073
12074       if (graphic_info[graphic2].anim_global_sync)
12075         GfxFrame[x][y] = FrameCounter;
12076       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12077         GfxFrame[x][y] = CustomValue[x][y];
12078       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12079         GfxFrame[x][y] = element_info[element2].collect_score;
12080       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12081         GfxFrame[x][y] = ChangeDelay[x][y];
12082
12083       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12084         DrawLevelGraphicAnimation(x, y, graphic2);
12085     }
12086 #else
12087     ResetGfxFrame(x, y, TRUE);
12088 #endif
12089
12090 #if 1
12091     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12092         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12093       ResetRandomAnimationValue(x, y);
12094 #endif
12095
12096 #if 1
12097     SetRandomAnimationValue(x, y);
12098 #endif
12099
12100 #if 1
12101     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12102 #endif
12103   }
12104 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12105
12106 #if 0
12107   debug_print_timestamp(0, "- time for TEST loop:     -->");
12108 #endif
12109
12110   SCAN_PLAYFIELD(x, y)
12111   {
12112     element = Feld[x][y];
12113     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12114
12115     ResetGfxFrame(x, y, TRUE);
12116
12117     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12118         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12119       ResetRandomAnimationValue(x, y);
12120
12121     SetRandomAnimationValue(x, y);
12122
12123     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12124
12125     if (IS_INACTIVE(element))
12126     {
12127       if (IS_ANIMATED(graphic))
12128         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12129
12130       continue;
12131     }
12132
12133     /* this may take place after moving, so 'element' may have changed */
12134     if (IS_CHANGING(x, y) &&
12135         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12136     {
12137       int page = element_info[element].event_page_nr[CE_DELAY];
12138
12139 #if 1
12140       HandleElementChange(x, y, page);
12141 #else
12142       if (CAN_CHANGE(element))
12143         HandleElementChange(x, y, page);
12144
12145       if (HAS_ACTION(element))
12146         ExecuteCustomElementAction(x, y, element, page);
12147 #endif
12148
12149       element = Feld[x][y];
12150       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12151     }
12152
12153 #if 0   // ---------------------------------------------------------------------
12154
12155     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12156     {
12157       StartMoving(x, y);
12158
12159       element = Feld[x][y];
12160       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12161
12162       if (IS_ANIMATED(graphic) &&
12163           !IS_MOVING(x, y) &&
12164           !Stop[x][y])
12165         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12166
12167       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12168         TEST_DrawTwinkleOnField(x, y);
12169     }
12170     else if (IS_MOVING(x, y))
12171       ContinueMoving(x, y);
12172     else
12173     {
12174       switch (element)
12175       {
12176         case EL_ACID:
12177         case EL_EXIT_OPEN:
12178         case EL_EM_EXIT_OPEN:
12179         case EL_SP_EXIT_OPEN:
12180         case EL_STEEL_EXIT_OPEN:
12181         case EL_EM_STEEL_EXIT_OPEN:
12182         case EL_SP_TERMINAL:
12183         case EL_SP_TERMINAL_ACTIVE:
12184         case EL_EXTRA_TIME:
12185         case EL_SHIELD_NORMAL:
12186         case EL_SHIELD_DEADLY:
12187           if (IS_ANIMATED(graphic))
12188             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12189           break;
12190
12191         case EL_DYNAMITE_ACTIVE:
12192         case EL_EM_DYNAMITE_ACTIVE:
12193         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12194         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12195         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12196         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12197         case EL_SP_DISK_RED_ACTIVE:
12198           CheckDynamite(x, y);
12199           break;
12200
12201         case EL_AMOEBA_GROWING:
12202           AmoebeWaechst(x, y);
12203           break;
12204
12205         case EL_AMOEBA_SHRINKING:
12206           AmoebaDisappearing(x, y);
12207           break;
12208
12209 #if !USE_NEW_AMOEBA_CODE
12210         case EL_AMOEBA_WET:
12211         case EL_AMOEBA_DRY:
12212         case EL_AMOEBA_FULL:
12213         case EL_BD_AMOEBA:
12214         case EL_EMC_DRIPPER:
12215           AmoebeAbleger(x, y);
12216           break;
12217 #endif
12218
12219         case EL_GAME_OF_LIFE:
12220         case EL_BIOMAZE:
12221           Life(x, y);
12222           break;
12223
12224         case EL_EXIT_CLOSED:
12225           CheckExit(x, y);
12226           break;
12227
12228         case EL_EM_EXIT_CLOSED:
12229           CheckExitEM(x, y);
12230           break;
12231
12232         case EL_STEEL_EXIT_CLOSED:
12233           CheckExitSteel(x, y);
12234           break;
12235
12236         case EL_EM_STEEL_EXIT_CLOSED:
12237           CheckExitSteelEM(x, y);
12238           break;
12239
12240         case EL_SP_EXIT_CLOSED:
12241           CheckExitSP(x, y);
12242           break;
12243
12244         case EL_EXPANDABLE_WALL_GROWING:
12245         case EL_EXPANDABLE_STEELWALL_GROWING:
12246           MauerWaechst(x, y);
12247           break;
12248
12249         case EL_EXPANDABLE_WALL:
12250         case EL_EXPANDABLE_WALL_HORIZONTAL:
12251         case EL_EXPANDABLE_WALL_VERTICAL:
12252         case EL_EXPANDABLE_WALL_ANY:
12253         case EL_BD_EXPANDABLE_WALL:
12254           MauerAbleger(x, y);
12255           break;
12256
12257         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12258         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12259         case EL_EXPANDABLE_STEELWALL_ANY:
12260           MauerAblegerStahl(x, y);
12261           break;
12262
12263         case EL_FLAMES:
12264           CheckForDragon(x, y);
12265           break;
12266
12267         case EL_EXPLOSION:
12268           break;
12269
12270         case EL_ELEMENT_SNAPPING:
12271         case EL_DIAGONAL_SHRINKING:
12272         case EL_DIAGONAL_GROWING:
12273         {
12274           graphic =
12275             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12276
12277           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12278           break;
12279         }
12280
12281         default:
12282           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12283             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12284           break;
12285       }
12286     }
12287
12288 #else   // ---------------------------------------------------------------------
12289
12290     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12291     {
12292       StartMoving(x, y);
12293
12294       element = Feld[x][y];
12295       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12296
12297       if (IS_ANIMATED(graphic) &&
12298           !IS_MOVING(x, y) &&
12299           !Stop[x][y])
12300         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12301
12302       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12303         TEST_DrawTwinkleOnField(x, y);
12304     }
12305     else if ((element == EL_ACID ||
12306               element == EL_EXIT_OPEN ||
12307               element == EL_EM_EXIT_OPEN ||
12308               element == EL_SP_EXIT_OPEN ||
12309               element == EL_STEEL_EXIT_OPEN ||
12310               element == EL_EM_STEEL_EXIT_OPEN ||
12311               element == EL_SP_TERMINAL ||
12312               element == EL_SP_TERMINAL_ACTIVE ||
12313               element == EL_EXTRA_TIME ||
12314               element == EL_SHIELD_NORMAL ||
12315               element == EL_SHIELD_DEADLY) &&
12316              IS_ANIMATED(graphic))
12317       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12318     else if (IS_MOVING(x, y))
12319       ContinueMoving(x, y);
12320     else if (IS_ACTIVE_BOMB(element))
12321       CheckDynamite(x, y);
12322     else if (element == EL_AMOEBA_GROWING)
12323       AmoebeWaechst(x, y);
12324     else if (element == EL_AMOEBA_SHRINKING)
12325       AmoebaDisappearing(x, y);
12326
12327 #if !USE_NEW_AMOEBA_CODE
12328     else if (IS_AMOEBALIVE(element))
12329       AmoebeAbleger(x, y);
12330 #endif
12331
12332     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12333       Life(x, y);
12334     else if (element == EL_EXIT_CLOSED)
12335       CheckExit(x, y);
12336     else if (element == EL_EM_EXIT_CLOSED)
12337       CheckExitEM(x, y);
12338     else if (element == EL_STEEL_EXIT_CLOSED)
12339       CheckExitSteel(x, y);
12340     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12341       CheckExitSteelEM(x, y);
12342     else if (element == EL_SP_EXIT_CLOSED)
12343       CheckExitSP(x, y);
12344     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12345              element == EL_EXPANDABLE_STEELWALL_GROWING)
12346       MauerWaechst(x, y);
12347     else if (element == EL_EXPANDABLE_WALL ||
12348              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12349              element == EL_EXPANDABLE_WALL_VERTICAL ||
12350              element == EL_EXPANDABLE_WALL_ANY ||
12351              element == EL_BD_EXPANDABLE_WALL)
12352       MauerAbleger(x, y);
12353     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12354              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12355              element == EL_EXPANDABLE_STEELWALL_ANY)
12356       MauerAblegerStahl(x, y);
12357     else if (element == EL_FLAMES)
12358       CheckForDragon(x, y);
12359     else if (element == EL_EXPLOSION)
12360       ; /* drawing of correct explosion animation is handled separately */
12361     else if (element == EL_ELEMENT_SNAPPING ||
12362              element == EL_DIAGONAL_SHRINKING ||
12363              element == EL_DIAGONAL_GROWING)
12364     {
12365       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12366
12367       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12368     }
12369     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12370       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12371
12372 #endif  // ---------------------------------------------------------------------
12373
12374     if (IS_BELT_ACTIVE(element))
12375       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12376
12377     if (game.magic_wall_active)
12378     {
12379       int jx = local_player->jx, jy = local_player->jy;
12380
12381       /* play the element sound at the position nearest to the player */
12382       if ((element == EL_MAGIC_WALL_FULL ||
12383            element == EL_MAGIC_WALL_ACTIVE ||
12384            element == EL_MAGIC_WALL_EMPTYING ||
12385            element == EL_BD_MAGIC_WALL_FULL ||
12386            element == EL_BD_MAGIC_WALL_ACTIVE ||
12387            element == EL_BD_MAGIC_WALL_EMPTYING ||
12388            element == EL_DC_MAGIC_WALL_FULL ||
12389            element == EL_DC_MAGIC_WALL_ACTIVE ||
12390            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12391           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12392       {
12393         magic_wall_x = x;
12394         magic_wall_y = y;
12395       }
12396     }
12397   }
12398
12399 #if 0
12400   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12401 #endif
12402
12403 #if USE_NEW_AMOEBA_CODE
12404   /* new experimental amoeba growth stuff */
12405   if (!(FrameCounter % 8))
12406   {
12407     static unsigned long random = 1684108901;
12408
12409     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12410     {
12411       x = RND(lev_fieldx);
12412       y = RND(lev_fieldy);
12413       element = Feld[x][y];
12414
12415       if (!IS_PLAYER(x,y) &&
12416           (element == EL_EMPTY ||
12417            CAN_GROW_INTO(element) ||
12418            element == EL_QUICKSAND_EMPTY ||
12419            element == EL_QUICKSAND_FAST_EMPTY ||
12420            element == EL_ACID_SPLASH_LEFT ||
12421            element == EL_ACID_SPLASH_RIGHT))
12422       {
12423         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12424             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12425             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12426             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12427           Feld[x][y] = EL_AMOEBA_DROP;
12428       }
12429
12430       random = random * 129 + 1;
12431     }
12432   }
12433 #endif
12434
12435 #if 0
12436   if (game.explosions_delayed)
12437 #endif
12438   {
12439     game.explosions_delayed = FALSE;
12440
12441     SCAN_PLAYFIELD(x, y)
12442     {
12443       element = Feld[x][y];
12444
12445       if (ExplodeField[x][y])
12446         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12447       else if (element == EL_EXPLOSION)
12448         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12449
12450       ExplodeField[x][y] = EX_TYPE_NONE;
12451     }
12452
12453     game.explosions_delayed = TRUE;
12454   }
12455
12456   if (game.magic_wall_active)
12457   {
12458     if (!(game.magic_wall_time_left % 4))
12459     {
12460       int element = Feld[magic_wall_x][magic_wall_y];
12461
12462       if (element == EL_BD_MAGIC_WALL_FULL ||
12463           element == EL_BD_MAGIC_WALL_ACTIVE ||
12464           element == EL_BD_MAGIC_WALL_EMPTYING)
12465         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12466       else if (element == EL_DC_MAGIC_WALL_FULL ||
12467                element == EL_DC_MAGIC_WALL_ACTIVE ||
12468                element == EL_DC_MAGIC_WALL_EMPTYING)
12469         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12470       else
12471         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12472     }
12473
12474     if (game.magic_wall_time_left > 0)
12475     {
12476       game.magic_wall_time_left--;
12477
12478       if (!game.magic_wall_time_left)
12479       {
12480         SCAN_PLAYFIELD(x, y)
12481         {
12482           element = Feld[x][y];
12483
12484           if (element == EL_MAGIC_WALL_ACTIVE ||
12485               element == EL_MAGIC_WALL_FULL)
12486           {
12487             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12488             TEST_DrawLevelField(x, y);
12489           }
12490           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12491                    element == EL_BD_MAGIC_WALL_FULL)
12492           {
12493             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12494             TEST_DrawLevelField(x, y);
12495           }
12496           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12497                    element == EL_DC_MAGIC_WALL_FULL)
12498           {
12499             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12500             TEST_DrawLevelField(x, y);
12501           }
12502         }
12503
12504         game.magic_wall_active = FALSE;
12505       }
12506     }
12507   }
12508
12509   if (game.light_time_left > 0)
12510   {
12511     game.light_time_left--;
12512
12513     if (game.light_time_left == 0)
12514       RedrawAllLightSwitchesAndInvisibleElements();
12515   }
12516
12517   if (game.timegate_time_left > 0)
12518   {
12519     game.timegate_time_left--;
12520
12521     if (game.timegate_time_left == 0)
12522       CloseAllOpenTimegates();
12523   }
12524
12525   if (game.lenses_time_left > 0)
12526   {
12527     game.lenses_time_left--;
12528
12529     if (game.lenses_time_left == 0)
12530       RedrawAllInvisibleElementsForLenses();
12531   }
12532
12533   if (game.magnify_time_left > 0)
12534   {
12535     game.magnify_time_left--;
12536
12537     if (game.magnify_time_left == 0)
12538       RedrawAllInvisibleElementsForMagnifier();
12539   }
12540
12541   for (i = 0; i < MAX_PLAYERS; i++)
12542   {
12543     struct PlayerInfo *player = &stored_player[i];
12544
12545     if (SHIELD_ON(player))
12546     {
12547       if (player->shield_deadly_time_left)
12548         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12549       else if (player->shield_normal_time_left)
12550         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12551     }
12552   }
12553
12554 #if USE_DELAYED_GFX_REDRAW
12555   SCAN_PLAYFIELD(x, y)
12556   {
12557 #if 1
12558     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12559 #else
12560     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12561         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12562 #endif
12563     {
12564       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12565         DrawLevelField(x, y);
12566
12567       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12568         DrawLevelFieldCrumbledSand(x, y);
12569
12570       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12571         DrawLevelFieldCrumbledSandNeighbours(x, y);
12572
12573       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12574         DrawTwinkleOnField(x, y);
12575     }
12576
12577     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12578   }
12579 #endif
12580
12581   CheckLevelTime();
12582
12583   DrawAllPlayers();
12584   PlayAllPlayersSound();
12585
12586   if (options.debug)                    /* calculate frames per second */
12587   {
12588     static unsigned long fps_counter = 0;
12589     static int fps_frames = 0;
12590     unsigned long fps_delay_ms = Counter() - fps_counter;
12591
12592     fps_frames++;
12593
12594     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12595     {
12596       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12597
12598       fps_frames = 0;
12599       fps_counter = Counter();
12600     }
12601
12602     redraw_mask |= REDRAW_FPS;
12603   }
12604
12605   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12606
12607   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12608   {
12609     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12610
12611     local_player->show_envelope = 0;
12612   }
12613
12614 #if 0
12615   debug_print_timestamp(0, "stop main loop profiling ");
12616   printf("----------------------------------------------------------\n");
12617 #endif
12618
12619   /* use random number generator in every frame to make it less predictable */
12620   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12621     RND(1);
12622 }
12623
12624 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12625 {
12626   int min_x = x, min_y = y, max_x = x, max_y = y;
12627   int i;
12628
12629   for (i = 0; i < MAX_PLAYERS; i++)
12630   {
12631     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12632
12633     if (!stored_player[i].active || &stored_player[i] == player)
12634       continue;
12635
12636     min_x = MIN(min_x, jx);
12637     min_y = MIN(min_y, jy);
12638     max_x = MAX(max_x, jx);
12639     max_y = MAX(max_y, jy);
12640   }
12641
12642   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12643 }
12644
12645 static boolean AllPlayersInVisibleScreen()
12646 {
12647   int i;
12648
12649   for (i = 0; i < MAX_PLAYERS; i++)
12650   {
12651     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12652
12653     if (!stored_player[i].active)
12654       continue;
12655
12656     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12657       return FALSE;
12658   }
12659
12660   return TRUE;
12661 }
12662
12663 void ScrollLevel(int dx, int dy)
12664 {
12665 #if 1
12666   static Bitmap *bitmap_db_field2 = NULL;
12667   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12668   int x, y;
12669 #else
12670   int i, x, y;
12671 #endif
12672
12673 #if 0
12674   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12675   /* only horizontal XOR vertical scroll direction allowed */
12676   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12677     return;
12678 #endif
12679
12680 #if 1
12681   if (bitmap_db_field2 == NULL)
12682     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12683
12684   /* needed when blitting directly to same bitmap -- should not be needed with
12685      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12686   BlitBitmap(drawto_field, bitmap_db_field2,
12687              FX + TILEX * (dx == -1) - softscroll_offset,
12688              FY + TILEY * (dy == -1) - softscroll_offset,
12689              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12690              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12691              FX + TILEX * (dx == 1) - softscroll_offset,
12692              FY + TILEY * (dy == 1) - softscroll_offset);
12693   BlitBitmap(bitmap_db_field2, drawto_field,
12694              FX + TILEX * (dx == 1) - softscroll_offset,
12695              FY + TILEY * (dy == 1) - softscroll_offset,
12696              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12697              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12698              FX + TILEX * (dx == 1) - softscroll_offset,
12699              FY + TILEY * (dy == 1) - softscroll_offset);
12700
12701 #else
12702
12703 #if 0
12704   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12705   int xsize = (BX2 - BX1 + 1);
12706   int ysize = (BY2 - BY1 + 1);
12707   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12708   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12709   int step  = (start < end ? +1 : -1);
12710
12711   for (i = start; i != end; i += step)
12712   {
12713     BlitBitmap(drawto_field, drawto_field,
12714                FX + TILEX * (dx != 0 ? i + step : 0),
12715                FY + TILEY * (dy != 0 ? i + step : 0),
12716                TILEX * (dx != 0 ? 1 : xsize),
12717                TILEY * (dy != 0 ? 1 : ysize),
12718                FX + TILEX * (dx != 0 ? i : 0),
12719                FY + TILEY * (dy != 0 ? i : 0));
12720   }
12721
12722 #else
12723
12724   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12725
12726   BlitBitmap(drawto_field, drawto_field,
12727              FX + TILEX * (dx == -1) - softscroll_offset,
12728              FY + TILEY * (dy == -1) - softscroll_offset,
12729              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12730              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12731              FX + TILEX * (dx == 1) - softscroll_offset,
12732              FY + TILEY * (dy == 1) - softscroll_offset);
12733 #endif
12734 #endif
12735
12736   if (dx != 0)
12737   {
12738     x = (dx == 1 ? BX1 : BX2);
12739     for (y = BY1; y <= BY2; y++)
12740       DrawScreenField(x, y);
12741   }
12742
12743   if (dy != 0)
12744   {
12745     y = (dy == 1 ? BY1 : BY2);
12746     for (x = BX1; x <= BX2; x++)
12747       DrawScreenField(x, y);
12748   }
12749
12750   redraw_mask |= REDRAW_FIELD;
12751 }
12752
12753 static boolean canFallDown(struct PlayerInfo *player)
12754 {
12755   int jx = player->jx, jy = player->jy;
12756
12757   return (IN_LEV_FIELD(jx, jy + 1) &&
12758           (IS_FREE(jx, jy + 1) ||
12759            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12760           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12761           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12762 }
12763
12764 static boolean canPassField(int x, int y, int move_dir)
12765 {
12766   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12767   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12768   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12769   int nextx = x + dx;
12770   int nexty = y + dy;
12771   int element = Feld[x][y];
12772
12773   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12774           !CAN_MOVE(element) &&
12775           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12776           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12777           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12778 }
12779
12780 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12781 {
12782   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12783   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12784   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12785   int newx = x + dx;
12786   int newy = y + dy;
12787
12788   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12789           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12790           (IS_DIGGABLE(Feld[newx][newy]) ||
12791            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12792            canPassField(newx, newy, move_dir)));
12793 }
12794
12795 static void CheckGravityMovement(struct PlayerInfo *player)
12796 {
12797 #if USE_PLAYER_GRAVITY
12798   if (player->gravity && !player->programmed_action)
12799 #else
12800   if (game.gravity && !player->programmed_action)
12801 #endif
12802   {
12803     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12804     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12805     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12806     int jx = player->jx, jy = player->jy;
12807     boolean player_is_moving_to_valid_field =
12808       (!player_is_snapping &&
12809        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12810         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12811     boolean player_can_fall_down = canFallDown(player);
12812
12813     if (player_can_fall_down &&
12814         !player_is_moving_to_valid_field)
12815       player->programmed_action = MV_DOWN;
12816   }
12817 }
12818
12819 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12820 {
12821   return CheckGravityMovement(player);
12822
12823 #if USE_PLAYER_GRAVITY
12824   if (player->gravity && !player->programmed_action)
12825 #else
12826   if (game.gravity && !player->programmed_action)
12827 #endif
12828   {
12829     int jx = player->jx, jy = player->jy;
12830     boolean field_under_player_is_free =
12831       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12832     boolean player_is_standing_on_valid_field =
12833       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12834        (IS_WALKABLE(Feld[jx][jy]) &&
12835         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12836
12837     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12838       player->programmed_action = MV_DOWN;
12839   }
12840 }
12841
12842 /*
12843   MovePlayerOneStep()
12844   -----------------------------------------------------------------------------
12845   dx, dy:               direction (non-diagonal) to try to move the player to
12846   real_dx, real_dy:     direction as read from input device (can be diagonal)
12847 */
12848
12849 boolean MovePlayerOneStep(struct PlayerInfo *player,
12850                           int dx, int dy, int real_dx, int real_dy)
12851 {
12852   int jx = player->jx, jy = player->jy;
12853   int new_jx = jx + dx, new_jy = jy + dy;
12854 #if !USE_FIXED_DONT_RUN_INTO
12855   int element;
12856 #endif
12857   int can_move;
12858   boolean player_can_move = !player->cannot_move;
12859
12860   if (!player->active || (!dx && !dy))
12861     return MP_NO_ACTION;
12862
12863   player->MovDir = (dx < 0 ? MV_LEFT :
12864                     dx > 0 ? MV_RIGHT :
12865                     dy < 0 ? MV_UP :
12866                     dy > 0 ? MV_DOWN :  MV_NONE);
12867
12868   if (!IN_LEV_FIELD(new_jx, new_jy))
12869     return MP_NO_ACTION;
12870
12871   if (!player_can_move)
12872   {
12873     if (player->MovPos == 0)
12874     {
12875       player->is_moving = FALSE;
12876       player->is_digging = FALSE;
12877       player->is_collecting = FALSE;
12878       player->is_snapping = FALSE;
12879       player->is_pushing = FALSE;
12880     }
12881   }
12882
12883 #if 1
12884   if (!options.network && game.centered_player_nr == -1 &&
12885       !AllPlayersInSight(player, new_jx, new_jy))
12886     return MP_NO_ACTION;
12887 #else
12888   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12889     return MP_NO_ACTION;
12890 #endif
12891
12892 #if !USE_FIXED_DONT_RUN_INTO
12893   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12894
12895   /* (moved to DigField()) */
12896   if (player_can_move && DONT_RUN_INTO(element))
12897   {
12898     if (element == EL_ACID && dx == 0 && dy == 1)
12899     {
12900       SplashAcid(new_jx, new_jy);
12901       Feld[jx][jy] = EL_PLAYER_1;
12902       InitMovingField(jx, jy, MV_DOWN);
12903       Store[jx][jy] = EL_ACID;
12904       ContinueMoving(jx, jy);
12905       BuryPlayer(player);
12906     }
12907     else
12908       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12909
12910     return MP_MOVING;
12911   }
12912 #endif
12913
12914   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12915   if (can_move != MP_MOVING)
12916     return can_move;
12917
12918   /* check if DigField() has caused relocation of the player */
12919   if (player->jx != jx || player->jy != jy)
12920     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12921
12922   StorePlayer[jx][jy] = 0;
12923   player->last_jx = jx;
12924   player->last_jy = jy;
12925   player->jx = new_jx;
12926   player->jy = new_jy;
12927   StorePlayer[new_jx][new_jy] = player->element_nr;
12928
12929   if (player->move_delay_value_next != -1)
12930   {
12931     player->move_delay_value = player->move_delay_value_next;
12932     player->move_delay_value_next = -1;
12933   }
12934
12935   player->MovPos =
12936     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12937
12938   player->step_counter++;
12939
12940   PlayerVisit[jx][jy] = FrameCounter;
12941
12942 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12943   player->is_moving = TRUE;
12944 #endif
12945
12946 #if 1
12947   /* should better be called in MovePlayer(), but this breaks some tapes */
12948   ScrollPlayer(player, SCROLL_INIT);
12949 #endif
12950
12951   return MP_MOVING;
12952 }
12953
12954 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12955 {
12956   int jx = player->jx, jy = player->jy;
12957   int old_jx = jx, old_jy = jy;
12958   int moved = MP_NO_ACTION;
12959
12960   if (!player->active)
12961     return FALSE;
12962
12963   if (!dx && !dy)
12964   {
12965     if (player->MovPos == 0)
12966     {
12967       player->is_moving = FALSE;
12968       player->is_digging = FALSE;
12969       player->is_collecting = FALSE;
12970       player->is_snapping = FALSE;
12971       player->is_pushing = FALSE;
12972     }
12973
12974     return FALSE;
12975   }
12976
12977   if (player->move_delay > 0)
12978     return FALSE;
12979
12980   player->move_delay = -1;              /* set to "uninitialized" value */
12981
12982   /* store if player is automatically moved to next field */
12983   player->is_auto_moving = (player->programmed_action != MV_NONE);
12984
12985   /* remove the last programmed player action */
12986   player->programmed_action = 0;
12987
12988   if (player->MovPos)
12989   {
12990     /* should only happen if pre-1.2 tape recordings are played */
12991     /* this is only for backward compatibility */
12992
12993     int original_move_delay_value = player->move_delay_value;
12994
12995 #if DEBUG
12996     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12997            tape.counter);
12998 #endif
12999
13000     /* scroll remaining steps with finest movement resolution */
13001     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13002
13003     while (player->MovPos)
13004     {
13005       ScrollPlayer(player, SCROLL_GO_ON);
13006       ScrollScreen(NULL, SCROLL_GO_ON);
13007
13008       AdvanceFrameAndPlayerCounters(player->index_nr);
13009
13010       DrawAllPlayers();
13011       BackToFront();
13012     }
13013
13014     player->move_delay_value = original_move_delay_value;
13015   }
13016
13017   player->is_active = FALSE;
13018
13019   if (player->last_move_dir & MV_HORIZONTAL)
13020   {
13021     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13022       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13023   }
13024   else
13025   {
13026     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13027       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13028   }
13029
13030 #if USE_FIXED_BORDER_RUNNING_GFX
13031   if (!moved && !player->is_active)
13032   {
13033     player->is_moving = FALSE;
13034     player->is_digging = FALSE;
13035     player->is_collecting = FALSE;
13036     player->is_snapping = FALSE;
13037     player->is_pushing = FALSE;
13038   }
13039 #endif
13040
13041   jx = player->jx;
13042   jy = player->jy;
13043
13044 #if 1
13045   if (moved & MP_MOVING && !ScreenMovPos &&
13046       (player->index_nr == game.centered_player_nr ||
13047        game.centered_player_nr == -1))
13048 #else
13049   if (moved & MP_MOVING && !ScreenMovPos &&
13050       (player == local_player || !options.network))
13051 #endif
13052   {
13053     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13054     int offset = game.scroll_delay_value;
13055
13056     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13057     {
13058       /* actual player has left the screen -- scroll in that direction */
13059       if (jx != old_jx)         /* player has moved horizontally */
13060         scroll_x += (jx - old_jx);
13061       else                      /* player has moved vertically */
13062         scroll_y += (jy - old_jy);
13063     }
13064     else
13065     {
13066       if (jx != old_jx)         /* player has moved horizontally */
13067       {
13068         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13069             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13070           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13071
13072         /* don't scroll over playfield boundaries */
13073         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13074           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13075
13076         /* don't scroll more than one field at a time */
13077         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13078
13079         /* don't scroll against the player's moving direction */
13080         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13081             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13082           scroll_x = old_scroll_x;
13083       }
13084       else                      /* player has moved vertically */
13085       {
13086         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13087             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13088           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13089
13090         /* don't scroll over playfield boundaries */
13091         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13092           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13093
13094         /* don't scroll more than one field at a time */
13095         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13096
13097         /* don't scroll against the player's moving direction */
13098         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13099             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13100           scroll_y = old_scroll_y;
13101       }
13102     }
13103
13104     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13105     {
13106 #if 1
13107       if (!options.network && game.centered_player_nr == -1 &&
13108           !AllPlayersInVisibleScreen())
13109       {
13110         scroll_x = old_scroll_x;
13111         scroll_y = old_scroll_y;
13112       }
13113       else
13114 #else
13115       if (!options.network && !AllPlayersInVisibleScreen())
13116       {
13117         scroll_x = old_scroll_x;
13118         scroll_y = old_scroll_y;
13119       }
13120       else
13121 #endif
13122       {
13123         ScrollScreen(player, SCROLL_INIT);
13124         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13125       }
13126     }
13127   }
13128
13129   player->StepFrame = 0;
13130
13131   if (moved & MP_MOVING)
13132   {
13133     if (old_jx != jx && old_jy == jy)
13134       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13135     else if (old_jx == jx && old_jy != jy)
13136       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13137
13138     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13139
13140     player->last_move_dir = player->MovDir;
13141     player->is_moving = TRUE;
13142     player->is_snapping = FALSE;
13143     player->is_switching = FALSE;
13144     player->is_dropping = FALSE;
13145     player->is_dropping_pressed = FALSE;
13146     player->drop_pressed_delay = 0;
13147
13148 #if 0
13149     /* should better be called here than above, but this breaks some tapes */
13150     ScrollPlayer(player, SCROLL_INIT);
13151 #endif
13152   }
13153   else
13154   {
13155     CheckGravityMovementWhenNotMoving(player);
13156
13157     player->is_moving = FALSE;
13158
13159     /* at this point, the player is allowed to move, but cannot move right now
13160        (e.g. because of something blocking the way) -- ensure that the player
13161        is also allowed to move in the next frame (in old versions before 3.1.1,
13162        the player was forced to wait again for eight frames before next try) */
13163
13164     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13165       player->move_delay = 0;   /* allow direct movement in the next frame */
13166   }
13167
13168   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13169     player->move_delay = player->move_delay_value;
13170
13171   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13172   {
13173     TestIfPlayerTouchesBadThing(jx, jy);
13174     TestIfPlayerTouchesCustomElement(jx, jy);
13175   }
13176
13177   if (!player->active)
13178     RemovePlayer(player);
13179
13180   return moved;
13181 }
13182
13183 void ScrollPlayer(struct PlayerInfo *player, int mode)
13184 {
13185   int jx = player->jx, jy = player->jy;
13186   int last_jx = player->last_jx, last_jy = player->last_jy;
13187   int move_stepsize = TILEX / player->move_delay_value;
13188
13189 #if USE_NEW_PLAYER_SPEED
13190   if (!player->active)
13191     return;
13192
13193   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13194     return;
13195 #else
13196   if (!player->active || player->MovPos == 0)
13197     return;
13198 #endif
13199
13200   if (mode == SCROLL_INIT)
13201   {
13202     player->actual_frame_counter = FrameCounter;
13203     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13204
13205     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13206         Feld[last_jx][last_jy] == EL_EMPTY)
13207     {
13208       int last_field_block_delay = 0;   /* start with no blocking at all */
13209       int block_delay_adjustment = player->block_delay_adjustment;
13210
13211       /* if player blocks last field, add delay for exactly one move */
13212       if (player->block_last_field)
13213       {
13214         last_field_block_delay += player->move_delay_value;
13215
13216         /* when blocking enabled, prevent moving up despite gravity */
13217 #if USE_PLAYER_GRAVITY
13218         if (player->gravity && player->MovDir == MV_UP)
13219           block_delay_adjustment = -1;
13220 #else
13221         if (game.gravity && player->MovDir == MV_UP)
13222           block_delay_adjustment = -1;
13223 #endif
13224       }
13225
13226       /* add block delay adjustment (also possible when not blocking) */
13227       last_field_block_delay += block_delay_adjustment;
13228
13229       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13230       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13231     }
13232
13233 #if USE_NEW_PLAYER_SPEED
13234     if (player->MovPos != 0)    /* player has not yet reached destination */
13235       return;
13236 #else
13237     return;
13238 #endif
13239   }
13240   else if (!FrameReached(&player->actual_frame_counter, 1))
13241     return;
13242
13243 #if USE_NEW_PLAYER_SPEED
13244   if (player->MovPos != 0)
13245   {
13246     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13247     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13248
13249     /* before DrawPlayer() to draw correct player graphic for this case */
13250     if (player->MovPos == 0)
13251       CheckGravityMovement(player);
13252   }
13253 #else
13254   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13255   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13256
13257   /* before DrawPlayer() to draw correct player graphic for this case */
13258   if (player->MovPos == 0)
13259     CheckGravityMovement(player);
13260 #endif
13261
13262   if (player->MovPos == 0)      /* player reached destination field */
13263   {
13264     if (player->move_delay_reset_counter > 0)
13265     {
13266       player->move_delay_reset_counter--;
13267
13268       if (player->move_delay_reset_counter == 0)
13269       {
13270         /* continue with normal speed after quickly moving through gate */
13271         HALVE_PLAYER_SPEED(player);
13272
13273         /* be able to make the next move without delay */
13274         player->move_delay = 0;
13275       }
13276     }
13277
13278     player->last_jx = jx;
13279     player->last_jy = jy;
13280
13281     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13282         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13283         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13284         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13285         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13286         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13287     {
13288       DrawPlayer(player);       /* needed here only to cleanup last field */
13289       RemovePlayer(player);
13290
13291       if (local_player->friends_still_needed == 0 ||
13292           IS_SP_ELEMENT(Feld[jx][jy]))
13293         PlayerWins(player);
13294     }
13295
13296     /* this breaks one level: "machine", level 000 */
13297     {
13298       int move_direction = player->MovDir;
13299       int enter_side = MV_DIR_OPPOSITE(move_direction);
13300       int leave_side = move_direction;
13301       int old_jx = last_jx;
13302       int old_jy = last_jy;
13303       int old_element = Feld[old_jx][old_jy];
13304       int new_element = Feld[jx][jy];
13305
13306       if (IS_CUSTOM_ELEMENT(old_element))
13307         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13308                                    CE_LEFT_BY_PLAYER,
13309                                    player->index_bit, leave_side);
13310
13311       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13312                                           CE_PLAYER_LEAVES_X,
13313                                           player->index_bit, leave_side);
13314
13315       if (IS_CUSTOM_ELEMENT(new_element))
13316         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13317                                    player->index_bit, enter_side);
13318
13319       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13320                                           CE_PLAYER_ENTERS_X,
13321                                           player->index_bit, enter_side);
13322
13323 #if USE_FIX_CE_ACTION_WITH_PLAYER
13324       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13325                                         CE_MOVE_OF_X, move_direction);
13326 #else
13327       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13328                                         CE_MOVE_OF_X, move_direction);
13329 #endif
13330     }
13331
13332     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13333     {
13334       TestIfPlayerTouchesBadThing(jx, jy);
13335       TestIfPlayerTouchesCustomElement(jx, jy);
13336
13337       /* needed because pushed element has not yet reached its destination,
13338          so it would trigger a change event at its previous field location */
13339       if (!player->is_pushing)
13340         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13341
13342       if (!player->active)
13343         RemovePlayer(player);
13344     }
13345
13346     if (!local_player->LevelSolved && level.use_step_counter)
13347     {
13348       int i;
13349
13350       TimePlayed++;
13351
13352       if (TimeLeft > 0)
13353       {
13354         TimeLeft--;
13355
13356         if (TimeLeft <= 10 && setup.time_limit)
13357           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13358
13359 #if 1
13360         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13361
13362         DisplayGameControlValues();
13363 #else
13364         DrawGameValue_Time(TimeLeft);
13365 #endif
13366
13367         if (!TimeLeft && setup.time_limit)
13368           for (i = 0; i < MAX_PLAYERS; i++)
13369             KillPlayer(&stored_player[i]);
13370       }
13371 #if 1
13372       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13373       {
13374         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13375
13376         DisplayGameControlValues();
13377       }
13378 #else
13379       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13380         DrawGameValue_Time(TimePlayed);
13381 #endif
13382     }
13383
13384     if (tape.single_step && tape.recording && !tape.pausing &&
13385         !player->programmed_action)
13386       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13387   }
13388 }
13389
13390 void ScrollScreen(struct PlayerInfo *player, int mode)
13391 {
13392   static unsigned long screen_frame_counter = 0;
13393
13394   if (mode == SCROLL_INIT)
13395   {
13396     /* set scrolling step size according to actual player's moving speed */
13397     ScrollStepSize = TILEX / player->move_delay_value;
13398
13399     screen_frame_counter = FrameCounter;
13400     ScreenMovDir = player->MovDir;
13401     ScreenMovPos = player->MovPos;
13402     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13403     return;
13404   }
13405   else if (!FrameReached(&screen_frame_counter, 1))
13406     return;
13407
13408   if (ScreenMovPos)
13409   {
13410     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13411     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13412     redraw_mask |= REDRAW_FIELD;
13413   }
13414   else
13415     ScreenMovDir = MV_NONE;
13416 }
13417
13418 void TestIfPlayerTouchesCustomElement(int x, int y)
13419 {
13420   static int xy[4][2] =
13421   {
13422     { 0, -1 },
13423     { -1, 0 },
13424     { +1, 0 },
13425     { 0, +1 }
13426   };
13427   static int trigger_sides[4][2] =
13428   {
13429     /* center side       border side */
13430     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13431     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13432     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13433     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13434   };
13435   static int touch_dir[4] =
13436   {
13437     MV_LEFT | MV_RIGHT,
13438     MV_UP   | MV_DOWN,
13439     MV_UP   | MV_DOWN,
13440     MV_LEFT | MV_RIGHT
13441   };
13442   int center_element = Feld[x][y];      /* should always be non-moving! */
13443   int i;
13444
13445   for (i = 0; i < NUM_DIRECTIONS; i++)
13446   {
13447     int xx = x + xy[i][0];
13448     int yy = y + xy[i][1];
13449     int center_side = trigger_sides[i][0];
13450     int border_side = trigger_sides[i][1];
13451     int border_element;
13452
13453     if (!IN_LEV_FIELD(xx, yy))
13454       continue;
13455
13456     if (IS_PLAYER(x, y))                /* player found at center element */
13457     {
13458       struct PlayerInfo *player = PLAYERINFO(x, y);
13459
13460       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13461         border_element = Feld[xx][yy];          /* may be moving! */
13462       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13463         border_element = Feld[xx][yy];
13464       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13465         border_element = MovingOrBlocked2Element(xx, yy);
13466       else
13467         continue;               /* center and border element do not touch */
13468
13469       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13470                                  player->index_bit, border_side);
13471       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13472                                           CE_PLAYER_TOUCHES_X,
13473                                           player->index_bit, border_side);
13474
13475 #if USE_FIX_CE_ACTION_WITH_PLAYER
13476       {
13477         /* use player element that is initially defined in the level playfield,
13478            not the player element that corresponds to the runtime player number
13479            (example: a level that contains EL_PLAYER_3 as the only player would
13480            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13481         int player_element = PLAYERINFO(x, y)->initial_element;
13482
13483         CheckElementChangeBySide(xx, yy, border_element, player_element,
13484                                  CE_TOUCHING_X, border_side);
13485       }
13486 #endif
13487     }
13488     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13489     {
13490       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13491
13492       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13493       {
13494         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13495           continue;             /* center and border element do not touch */
13496       }
13497
13498       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13499                                  player->index_bit, center_side);
13500       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13501                                           CE_PLAYER_TOUCHES_X,
13502                                           player->index_bit, center_side);
13503
13504 #if USE_FIX_CE_ACTION_WITH_PLAYER
13505       {
13506         /* use player element that is initially defined in the level playfield,
13507            not the player element that corresponds to the runtime player number
13508            (example: a level that contains EL_PLAYER_3 as the only player would
13509            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13510         int player_element = PLAYERINFO(xx, yy)->initial_element;
13511
13512         CheckElementChangeBySide(x, y, center_element, player_element,
13513                                  CE_TOUCHING_X, center_side);
13514       }
13515 #endif
13516
13517       break;
13518     }
13519   }
13520 }
13521
13522 #if USE_ELEMENT_TOUCHING_BUGFIX
13523
13524 void TestIfElementTouchesCustomElement(int x, int y)
13525 {
13526   static int xy[4][2] =
13527   {
13528     { 0, -1 },
13529     { -1, 0 },
13530     { +1, 0 },
13531     { 0, +1 }
13532   };
13533   static int trigger_sides[4][2] =
13534   {
13535     /* center side      border side */
13536     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13537     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13538     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13539     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13540   };
13541   static int touch_dir[4] =
13542   {
13543     MV_LEFT | MV_RIGHT,
13544     MV_UP   | MV_DOWN,
13545     MV_UP   | MV_DOWN,
13546     MV_LEFT | MV_RIGHT
13547   };
13548   boolean change_center_element = FALSE;
13549   int center_element = Feld[x][y];      /* should always be non-moving! */
13550   int border_element_old[NUM_DIRECTIONS];
13551   int i;
13552
13553   for (i = 0; i < NUM_DIRECTIONS; i++)
13554   {
13555     int xx = x + xy[i][0];
13556     int yy = y + xy[i][1];
13557     int border_element;
13558
13559     border_element_old[i] = -1;
13560
13561     if (!IN_LEV_FIELD(xx, yy))
13562       continue;
13563
13564     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13565       border_element = Feld[xx][yy];    /* may be moving! */
13566     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13567       border_element = Feld[xx][yy];
13568     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13569       border_element = MovingOrBlocked2Element(xx, yy);
13570     else
13571       continue;                 /* center and border element do not touch */
13572
13573     border_element_old[i] = border_element;
13574   }
13575
13576   for (i = 0; i < NUM_DIRECTIONS; i++)
13577   {
13578     int xx = x + xy[i][0];
13579     int yy = y + xy[i][1];
13580     int center_side = trigger_sides[i][0];
13581     int border_element = border_element_old[i];
13582
13583     if (border_element == -1)
13584       continue;
13585
13586     /* check for change of border element */
13587     CheckElementChangeBySide(xx, yy, border_element, center_element,
13588                              CE_TOUCHING_X, center_side);
13589   }
13590
13591   for (i = 0; i < NUM_DIRECTIONS; i++)
13592   {
13593     int xx = x + xy[i][0];
13594     int yy = y + xy[i][1];
13595     int border_side = trigger_sides[i][1];
13596     int border_element = border_element_old[i];
13597
13598     if (border_element == -1)
13599       continue;
13600
13601     /* check for change of center element (but change it only once) */
13602     if (!change_center_element)
13603       change_center_element =
13604         CheckElementChangeBySide(x, y, center_element, border_element,
13605                                  CE_TOUCHING_X, border_side);
13606
13607 #if USE_FIX_CE_ACTION_WITH_PLAYER
13608     if (IS_PLAYER(xx, yy))
13609     {
13610       /* use player element that is initially defined in the level playfield,
13611          not the player element that corresponds to the runtime player number
13612          (example: a level that contains EL_PLAYER_3 as the only player would
13613          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13614       int player_element = PLAYERINFO(xx, yy)->initial_element;
13615
13616       CheckElementChangeBySide(x, y, center_element, player_element,
13617                                CE_TOUCHING_X, border_side);
13618     }
13619 #endif
13620   }
13621 }
13622
13623 #else
13624
13625 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13626 {
13627   static int xy[4][2] =
13628   {
13629     { 0, -1 },
13630     { -1, 0 },
13631     { +1, 0 },
13632     { 0, +1 }
13633   };
13634   static int trigger_sides[4][2] =
13635   {
13636     /* center side      border side */
13637     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13638     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13639     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13640     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13641   };
13642   static int touch_dir[4] =
13643   {
13644     MV_LEFT | MV_RIGHT,
13645     MV_UP   | MV_DOWN,
13646     MV_UP   | MV_DOWN,
13647     MV_LEFT | MV_RIGHT
13648   };
13649   boolean change_center_element = FALSE;
13650   int center_element = Feld[x][y];      /* should always be non-moving! */
13651   int i;
13652
13653   for (i = 0; i < NUM_DIRECTIONS; i++)
13654   {
13655     int xx = x + xy[i][0];
13656     int yy = y + xy[i][1];
13657     int center_side = trigger_sides[i][0];
13658     int border_side = trigger_sides[i][1];
13659     int border_element;
13660
13661     if (!IN_LEV_FIELD(xx, yy))
13662       continue;
13663
13664     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13665       border_element = Feld[xx][yy];    /* may be moving! */
13666     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13667       border_element = Feld[xx][yy];
13668     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13669       border_element = MovingOrBlocked2Element(xx, yy);
13670     else
13671       continue;                 /* center and border element do not touch */
13672
13673     /* check for change of center element (but change it only once) */
13674     if (!change_center_element)
13675       change_center_element =
13676         CheckElementChangeBySide(x, y, center_element, border_element,
13677                                  CE_TOUCHING_X, border_side);
13678
13679     /* check for change of border element */
13680     CheckElementChangeBySide(xx, yy, border_element, center_element,
13681                              CE_TOUCHING_X, center_side);
13682   }
13683 }
13684
13685 #endif
13686
13687 void TestIfElementHitsCustomElement(int x, int y, int direction)
13688 {
13689   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13690   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13691   int hitx = x + dx, hity = y + dy;
13692   int hitting_element = Feld[x][y];
13693   int touched_element;
13694
13695   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13696     return;
13697
13698   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13699                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13700
13701   if (IN_LEV_FIELD(hitx, hity))
13702   {
13703     int opposite_direction = MV_DIR_OPPOSITE(direction);
13704     int hitting_side = direction;
13705     int touched_side = opposite_direction;
13706     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13707                           MovDir[hitx][hity] != direction ||
13708                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13709
13710     object_hit = TRUE;
13711
13712     if (object_hit)
13713     {
13714       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13715                                CE_HITTING_X, touched_side);
13716
13717       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13718                                CE_HIT_BY_X, hitting_side);
13719
13720       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13721                                CE_HIT_BY_SOMETHING, opposite_direction);
13722
13723 #if USE_FIX_CE_ACTION_WITH_PLAYER
13724       if (IS_PLAYER(hitx, hity))
13725       {
13726         /* use player element that is initially defined in the level playfield,
13727            not the player element that corresponds to the runtime player number
13728            (example: a level that contains EL_PLAYER_3 as the only player would
13729            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13730         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13731
13732         CheckElementChangeBySide(x, y, hitting_element, player_element,
13733                                  CE_HITTING_X, touched_side);
13734       }
13735 #endif
13736     }
13737   }
13738
13739   /* "hitting something" is also true when hitting the playfield border */
13740   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13741                            CE_HITTING_SOMETHING, direction);
13742 }
13743
13744 #if 0
13745 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13746 {
13747   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13748   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13749   int hitx = x + dx, hity = y + dy;
13750   int hitting_element = Feld[x][y];
13751   int touched_element;
13752 #if 0
13753   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13754                         !IS_FREE(hitx, hity) &&
13755                         (!IS_MOVING(hitx, hity) ||
13756                          MovDir[hitx][hity] != direction ||
13757                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13758 #endif
13759
13760   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13761     return;
13762
13763 #if 0
13764   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13765     return;
13766 #endif
13767
13768   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13769                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13770
13771   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13772                            EP_CAN_SMASH_EVERYTHING, direction);
13773
13774   if (IN_LEV_FIELD(hitx, hity))
13775   {
13776     int opposite_direction = MV_DIR_OPPOSITE(direction);
13777     int hitting_side = direction;
13778     int touched_side = opposite_direction;
13779 #if 0
13780     int touched_element = MovingOrBlocked2Element(hitx, hity);
13781 #endif
13782 #if 1
13783     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13784                           MovDir[hitx][hity] != direction ||
13785                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13786
13787     object_hit = TRUE;
13788 #endif
13789
13790     if (object_hit)
13791     {
13792       int i;
13793
13794       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13795                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13796
13797       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13798                                CE_OTHER_IS_SMASHING, touched_side);
13799
13800       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13801                                CE_OTHER_GETS_SMASHED, hitting_side);
13802     }
13803   }
13804 }
13805 #endif
13806
13807 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13808 {
13809   int i, kill_x = -1, kill_y = -1;
13810
13811   int bad_element = -1;
13812   static int test_xy[4][2] =
13813   {
13814     { 0, -1 },
13815     { -1, 0 },
13816     { +1, 0 },
13817     { 0, +1 }
13818   };
13819   static int test_dir[4] =
13820   {
13821     MV_UP,
13822     MV_LEFT,
13823     MV_RIGHT,
13824     MV_DOWN
13825   };
13826
13827   for (i = 0; i < NUM_DIRECTIONS; i++)
13828   {
13829     int test_x, test_y, test_move_dir, test_element;
13830
13831     test_x = good_x + test_xy[i][0];
13832     test_y = good_y + test_xy[i][1];
13833
13834     if (!IN_LEV_FIELD(test_x, test_y))
13835       continue;
13836
13837     test_move_dir =
13838       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13839
13840     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13841
13842     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13843        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13844     */
13845     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13846         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13847     {
13848       kill_x = test_x;
13849       kill_y = test_y;
13850       bad_element = test_element;
13851
13852       break;
13853     }
13854   }
13855
13856   if (kill_x != -1 || kill_y != -1)
13857   {
13858     if (IS_PLAYER(good_x, good_y))
13859     {
13860       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13861
13862       if (player->shield_deadly_time_left > 0 &&
13863           !IS_INDESTRUCTIBLE(bad_element))
13864         Bang(kill_x, kill_y);
13865       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13866         KillPlayer(player);
13867     }
13868     else
13869       Bang(good_x, good_y);
13870   }
13871 }
13872
13873 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13874 {
13875   int i, kill_x = -1, kill_y = -1;
13876   int bad_element = Feld[bad_x][bad_y];
13877   static int test_xy[4][2] =
13878   {
13879     { 0, -1 },
13880     { -1, 0 },
13881     { +1, 0 },
13882     { 0, +1 }
13883   };
13884   static int touch_dir[4] =
13885   {
13886     MV_LEFT | MV_RIGHT,
13887     MV_UP   | MV_DOWN,
13888     MV_UP   | MV_DOWN,
13889     MV_LEFT | MV_RIGHT
13890   };
13891   static int test_dir[4] =
13892   {
13893     MV_UP,
13894     MV_LEFT,
13895     MV_RIGHT,
13896     MV_DOWN
13897   };
13898
13899   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13900     return;
13901
13902   for (i = 0; i < NUM_DIRECTIONS; i++)
13903   {
13904     int test_x, test_y, test_move_dir, test_element;
13905
13906     test_x = bad_x + test_xy[i][0];
13907     test_y = bad_y + test_xy[i][1];
13908     if (!IN_LEV_FIELD(test_x, test_y))
13909       continue;
13910
13911     test_move_dir =
13912       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13913
13914     test_element = Feld[test_x][test_y];
13915
13916     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13917        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13918     */
13919     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13920         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13921     {
13922       /* good thing is player or penguin that does not move away */
13923       if (IS_PLAYER(test_x, test_y))
13924       {
13925         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13926
13927         if (bad_element == EL_ROBOT && player->is_moving)
13928           continue;     /* robot does not kill player if he is moving */
13929
13930         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13931         {
13932           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13933             continue;           /* center and border element do not touch */
13934         }
13935
13936         kill_x = test_x;
13937         kill_y = test_y;
13938         break;
13939       }
13940       else if (test_element == EL_PENGUIN)
13941       {
13942         kill_x = test_x;
13943         kill_y = test_y;
13944         break;
13945       }
13946     }
13947   }
13948
13949   if (kill_x != -1 || kill_y != -1)
13950   {
13951     if (IS_PLAYER(kill_x, kill_y))
13952     {
13953       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13954
13955       if (player->shield_deadly_time_left > 0 &&
13956           !IS_INDESTRUCTIBLE(bad_element))
13957         Bang(bad_x, bad_y);
13958       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13959         KillPlayer(player);
13960     }
13961     else
13962       Bang(kill_x, kill_y);
13963   }
13964 }
13965
13966 void TestIfPlayerTouchesBadThing(int x, int y)
13967 {
13968   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13969 }
13970
13971 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13972 {
13973   TestIfGoodThingHitsBadThing(x, y, move_dir);
13974 }
13975
13976 void TestIfBadThingTouchesPlayer(int x, int y)
13977 {
13978   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13979 }
13980
13981 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13982 {
13983   TestIfBadThingHitsGoodThing(x, y, move_dir);
13984 }
13985
13986 void TestIfFriendTouchesBadThing(int x, int y)
13987 {
13988   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13989 }
13990
13991 void TestIfBadThingTouchesFriend(int x, int y)
13992 {
13993   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13994 }
13995
13996 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13997 {
13998   int i, kill_x = bad_x, kill_y = bad_y;
13999   static int xy[4][2] =
14000   {
14001     { 0, -1 },
14002     { -1, 0 },
14003     { +1, 0 },
14004     { 0, +1 }
14005   };
14006
14007   for (i = 0; i < NUM_DIRECTIONS; i++)
14008   {
14009     int x, y, element;
14010
14011     x = bad_x + xy[i][0];
14012     y = bad_y + xy[i][1];
14013     if (!IN_LEV_FIELD(x, y))
14014       continue;
14015
14016     element = Feld[x][y];
14017     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14018         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14019     {
14020       kill_x = x;
14021       kill_y = y;
14022       break;
14023     }
14024   }
14025
14026   if (kill_x != bad_x || kill_y != bad_y)
14027     Bang(bad_x, bad_y);
14028 }
14029
14030 void KillPlayer(struct PlayerInfo *player)
14031 {
14032   int jx = player->jx, jy = player->jy;
14033
14034   if (!player->active)
14035     return;
14036
14037   /* the following code was introduced to prevent an infinite loop when calling
14038      -> Bang()
14039      -> CheckTriggeredElementChangeExt()
14040      -> ExecuteCustomElementAction()
14041      -> KillPlayer()
14042      -> (infinitely repeating the above sequence of function calls)
14043      which occurs when killing the player while having a CE with the setting
14044      "kill player X when explosion of <player X>"; the solution using a new
14045      field "player->killed" was chosen for backwards compatibility, although
14046      clever use of the fields "player->active" etc. would probably also work */
14047 #if 1
14048   if (player->killed)
14049     return;
14050 #endif
14051
14052   player->killed = TRUE;
14053
14054   /* remove accessible field at the player's position */
14055   Feld[jx][jy] = EL_EMPTY;
14056
14057   /* deactivate shield (else Bang()/Explode() would not work right) */
14058   player->shield_normal_time_left = 0;
14059   player->shield_deadly_time_left = 0;
14060
14061   Bang(jx, jy);
14062   BuryPlayer(player);
14063 }
14064
14065 static void KillPlayerUnlessEnemyProtected(int x, int y)
14066 {
14067   if (!PLAYER_ENEMY_PROTECTED(x, y))
14068     KillPlayer(PLAYERINFO(x, y));
14069 }
14070
14071 static void KillPlayerUnlessExplosionProtected(int x, int y)
14072 {
14073   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14074     KillPlayer(PLAYERINFO(x, y));
14075 }
14076
14077 void BuryPlayer(struct PlayerInfo *player)
14078 {
14079   int jx = player->jx, jy = player->jy;
14080
14081   if (!player->active)
14082     return;
14083
14084   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14085   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14086
14087   player->GameOver = TRUE;
14088   RemovePlayer(player);
14089 }
14090
14091 void RemovePlayer(struct PlayerInfo *player)
14092 {
14093   int jx = player->jx, jy = player->jy;
14094   int i, found = FALSE;
14095
14096   player->present = FALSE;
14097   player->active = FALSE;
14098
14099   if (!ExplodeField[jx][jy])
14100     StorePlayer[jx][jy] = 0;
14101
14102   if (player->is_moving)
14103     TEST_DrawLevelField(player->last_jx, player->last_jy);
14104
14105   for (i = 0; i < MAX_PLAYERS; i++)
14106     if (stored_player[i].active)
14107       found = TRUE;
14108
14109   if (!found)
14110     AllPlayersGone = TRUE;
14111
14112   ExitX = ZX = jx;
14113   ExitY = ZY = jy;
14114 }
14115
14116 #if USE_NEW_SNAP_DELAY
14117 static void setFieldForSnapping(int x, int y, int element, int direction)
14118 {
14119   struct ElementInfo *ei = &element_info[element];
14120   int direction_bit = MV_DIR_TO_BIT(direction);
14121   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14122   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14123                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14124
14125   Feld[x][y] = EL_ELEMENT_SNAPPING;
14126   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14127
14128   ResetGfxAnimation(x, y);
14129
14130   GfxElement[x][y] = element;
14131   GfxAction[x][y] = action;
14132   GfxDir[x][y] = direction;
14133   GfxFrame[x][y] = -1;
14134 }
14135 #endif
14136
14137 /*
14138   =============================================================================
14139   checkDiagonalPushing()
14140   -----------------------------------------------------------------------------
14141   check if diagonal input device direction results in pushing of object
14142   (by checking if the alternative direction is walkable, diggable, ...)
14143   =============================================================================
14144 */
14145
14146 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14147                                     int x, int y, int real_dx, int real_dy)
14148 {
14149   int jx, jy, dx, dy, xx, yy;
14150
14151   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14152     return TRUE;
14153
14154   /* diagonal direction: check alternative direction */
14155   jx = player->jx;
14156   jy = player->jy;
14157   dx = x - jx;
14158   dy = y - jy;
14159   xx = jx + (dx == 0 ? real_dx : 0);
14160   yy = jy + (dy == 0 ? real_dy : 0);
14161
14162   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14163 }
14164
14165 /*
14166   =============================================================================
14167   DigField()
14168   -----------------------------------------------------------------------------
14169   x, y:                 field next to player (non-diagonal) to try to dig to
14170   real_dx, real_dy:     direction as read from input device (can be diagonal)
14171   =============================================================================
14172 */
14173
14174 static int DigField(struct PlayerInfo *player,
14175                     int oldx, int oldy, int x, int y,
14176                     int real_dx, int real_dy, int mode)
14177 {
14178   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14179   boolean player_was_pushing = player->is_pushing;
14180   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14181   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14182   int jx = oldx, jy = oldy;
14183   int dx = x - jx, dy = y - jy;
14184   int nextx = x + dx, nexty = y + dy;
14185   int move_direction = (dx == -1 ? MV_LEFT  :
14186                         dx == +1 ? MV_RIGHT :
14187                         dy == -1 ? MV_UP    :
14188                         dy == +1 ? MV_DOWN  : MV_NONE);
14189   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14190   int dig_side = MV_DIR_OPPOSITE(move_direction);
14191   int old_element = Feld[jx][jy];
14192 #if USE_FIXED_DONT_RUN_INTO
14193   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14194 #else
14195   int element;
14196 #endif
14197   int collect_count;
14198
14199   if (is_player)                /* function can also be called by EL_PENGUIN */
14200   {
14201     if (player->MovPos == 0)
14202     {
14203       player->is_digging = FALSE;
14204       player->is_collecting = FALSE;
14205     }
14206
14207     if (player->MovPos == 0)    /* last pushing move finished */
14208       player->is_pushing = FALSE;
14209
14210     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14211     {
14212       player->is_switching = FALSE;
14213       player->push_delay = -1;
14214
14215       return MP_NO_ACTION;
14216     }
14217   }
14218
14219 #if !USE_FIXED_DONT_RUN_INTO
14220   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14221     return MP_NO_ACTION;
14222 #endif
14223
14224   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14225     old_element = Back[jx][jy];
14226
14227   /* in case of element dropped at player position, check background */
14228   else if (Back[jx][jy] != EL_EMPTY &&
14229            game.engine_version >= VERSION_IDENT(2,2,0,0))
14230     old_element = Back[jx][jy];
14231
14232   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14233     return MP_NO_ACTION;        /* field has no opening in this direction */
14234
14235   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14236     return MP_NO_ACTION;        /* field has no opening in this direction */
14237
14238 #if USE_FIXED_DONT_RUN_INTO
14239   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14240   {
14241     SplashAcid(x, y);
14242
14243     Feld[jx][jy] = player->artwork_element;
14244     InitMovingField(jx, jy, MV_DOWN);
14245     Store[jx][jy] = EL_ACID;
14246     ContinueMoving(jx, jy);
14247     BuryPlayer(player);
14248
14249     return MP_DONT_RUN_INTO;
14250   }
14251 #endif
14252
14253 #if USE_FIXED_DONT_RUN_INTO
14254   if (player_can_move && DONT_RUN_INTO(element))
14255   {
14256     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14257
14258     return MP_DONT_RUN_INTO;
14259   }
14260 #endif
14261
14262 #if USE_FIXED_DONT_RUN_INTO
14263   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14264     return MP_NO_ACTION;
14265 #endif
14266
14267 #if !USE_FIXED_DONT_RUN_INTO
14268   element = Feld[x][y];
14269 #endif
14270
14271   collect_count = element_info[element].collect_count_initial;
14272
14273   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14274     return MP_NO_ACTION;
14275
14276   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14277     player_can_move = player_can_move_or_snap;
14278
14279   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14280       game.engine_version >= VERSION_IDENT(2,2,0,0))
14281   {
14282     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14283                                player->index_bit, dig_side);
14284     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14285                                         player->index_bit, dig_side);
14286
14287     if (element == EL_DC_LANDMINE)
14288       Bang(x, y);
14289
14290     if (Feld[x][y] != element)          /* field changed by snapping */
14291       return MP_ACTION;
14292
14293     return MP_NO_ACTION;
14294   }
14295
14296 #if USE_PLAYER_GRAVITY
14297   if (player->gravity && is_player && !player->is_auto_moving &&
14298       canFallDown(player) && move_direction != MV_DOWN &&
14299       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14300     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14301 #else
14302   if (game.gravity && is_player && !player->is_auto_moving &&
14303       canFallDown(player) && move_direction != MV_DOWN &&
14304       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14305     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14306 #endif
14307
14308   if (player_can_move &&
14309       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14310   {
14311     int sound_element = SND_ELEMENT(element);
14312     int sound_action = ACTION_WALKING;
14313
14314     if (IS_RND_GATE(element))
14315     {
14316       if (!player->key[RND_GATE_NR(element)])
14317         return MP_NO_ACTION;
14318     }
14319     else if (IS_RND_GATE_GRAY(element))
14320     {
14321       if (!player->key[RND_GATE_GRAY_NR(element)])
14322         return MP_NO_ACTION;
14323     }
14324     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14325     {
14326       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14327         return MP_NO_ACTION;
14328     }
14329     else if (element == EL_EXIT_OPEN ||
14330              element == EL_EM_EXIT_OPEN ||
14331              element == EL_STEEL_EXIT_OPEN ||
14332              element == EL_EM_STEEL_EXIT_OPEN ||
14333              element == EL_SP_EXIT_OPEN ||
14334              element == EL_SP_EXIT_OPENING)
14335     {
14336       sound_action = ACTION_PASSING;    /* player is passing exit */
14337     }
14338     else if (element == EL_EMPTY)
14339     {
14340       sound_action = ACTION_MOVING;             /* nothing to walk on */
14341     }
14342
14343     /* play sound from background or player, whatever is available */
14344     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14345       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14346     else
14347       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14348   }
14349   else if (player_can_move &&
14350            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14351   {
14352     if (!ACCESS_FROM(element, opposite_direction))
14353       return MP_NO_ACTION;      /* field not accessible from this direction */
14354
14355     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14356       return MP_NO_ACTION;
14357
14358     if (IS_EM_GATE(element))
14359     {
14360       if (!player->key[EM_GATE_NR(element)])
14361         return MP_NO_ACTION;
14362     }
14363     else if (IS_EM_GATE_GRAY(element))
14364     {
14365       if (!player->key[EM_GATE_GRAY_NR(element)])
14366         return MP_NO_ACTION;
14367     }
14368     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14369     {
14370       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14371         return MP_NO_ACTION;
14372     }
14373     else if (IS_EMC_GATE(element))
14374     {
14375       if (!player->key[EMC_GATE_NR(element)])
14376         return MP_NO_ACTION;
14377     }
14378     else if (IS_EMC_GATE_GRAY(element))
14379     {
14380       if (!player->key[EMC_GATE_GRAY_NR(element)])
14381         return MP_NO_ACTION;
14382     }
14383     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14384     {
14385       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14386         return MP_NO_ACTION;
14387     }
14388     else if (element == EL_DC_GATE_WHITE ||
14389              element == EL_DC_GATE_WHITE_GRAY ||
14390              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14391     {
14392       if (player->num_white_keys == 0)
14393         return MP_NO_ACTION;
14394
14395       player->num_white_keys--;
14396     }
14397     else if (IS_SP_PORT(element))
14398     {
14399       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14400           element == EL_SP_GRAVITY_PORT_RIGHT ||
14401           element == EL_SP_GRAVITY_PORT_UP ||
14402           element == EL_SP_GRAVITY_PORT_DOWN)
14403 #if USE_PLAYER_GRAVITY
14404         player->gravity = !player->gravity;
14405 #else
14406         game.gravity = !game.gravity;
14407 #endif
14408       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14409                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14410                element == EL_SP_GRAVITY_ON_PORT_UP ||
14411                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14412 #if USE_PLAYER_GRAVITY
14413         player->gravity = TRUE;
14414 #else
14415         game.gravity = TRUE;
14416 #endif
14417       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14418                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14419                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14420                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14421 #if USE_PLAYER_GRAVITY
14422         player->gravity = FALSE;
14423 #else
14424         game.gravity = FALSE;
14425 #endif
14426     }
14427
14428     /* automatically move to the next field with double speed */
14429     player->programmed_action = move_direction;
14430
14431     if (player->move_delay_reset_counter == 0)
14432     {
14433       player->move_delay_reset_counter = 2;     /* two double speed steps */
14434
14435       DOUBLE_PLAYER_SPEED(player);
14436     }
14437
14438     PlayLevelSoundAction(x, y, ACTION_PASSING);
14439   }
14440   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14441   {
14442     RemoveField(x, y);
14443
14444     if (mode != DF_SNAP)
14445     {
14446       GfxElement[x][y] = GFX_ELEMENT(element);
14447       player->is_digging = TRUE;
14448     }
14449
14450     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14451
14452     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14453                                         player->index_bit, dig_side);
14454
14455     if (mode == DF_SNAP)
14456     {
14457 #if USE_NEW_SNAP_DELAY
14458       if (level.block_snap_field)
14459         setFieldForSnapping(x, y, element, move_direction);
14460       else
14461         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14462 #else
14463       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14464 #endif
14465
14466       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14467                                           player->index_bit, dig_side);
14468     }
14469   }
14470   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14471   {
14472     RemoveField(x, y);
14473
14474     if (is_player && mode != DF_SNAP)
14475     {
14476       GfxElement[x][y] = element;
14477       player->is_collecting = TRUE;
14478     }
14479
14480     if (element == EL_SPEED_PILL)
14481     {
14482       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14483     }
14484     else if (element == EL_EXTRA_TIME && level.time > 0)
14485     {
14486       TimeLeft += level.extra_time;
14487
14488 #if 1
14489       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14490
14491       DisplayGameControlValues();
14492 #else
14493       DrawGameValue_Time(TimeLeft);
14494 #endif
14495     }
14496     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14497     {
14498       player->shield_normal_time_left += level.shield_normal_time;
14499       if (element == EL_SHIELD_DEADLY)
14500         player->shield_deadly_time_left += level.shield_deadly_time;
14501     }
14502     else if (element == EL_DYNAMITE ||
14503              element == EL_EM_DYNAMITE ||
14504              element == EL_SP_DISK_RED)
14505     {
14506       if (player->inventory_size < MAX_INVENTORY_SIZE)
14507         player->inventory_element[player->inventory_size++] = element;
14508
14509       DrawGameDoorValues();
14510     }
14511     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14512     {
14513       player->dynabomb_count++;
14514       player->dynabombs_left++;
14515     }
14516     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14517     {
14518       player->dynabomb_size++;
14519     }
14520     else if (element == EL_DYNABOMB_INCREASE_POWER)
14521     {
14522       player->dynabomb_xl = TRUE;
14523     }
14524     else if (IS_KEY(element))
14525     {
14526       player->key[KEY_NR(element)] = TRUE;
14527
14528       DrawGameDoorValues();
14529     }
14530     else if (element == EL_DC_KEY_WHITE)
14531     {
14532       player->num_white_keys++;
14533
14534       /* display white keys? */
14535       /* DrawGameDoorValues(); */
14536     }
14537     else if (IS_ENVELOPE(element))
14538     {
14539       player->show_envelope = element;
14540     }
14541     else if (element == EL_EMC_LENSES)
14542     {
14543       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14544
14545       RedrawAllInvisibleElementsForLenses();
14546     }
14547     else if (element == EL_EMC_MAGNIFIER)
14548     {
14549       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14550
14551       RedrawAllInvisibleElementsForMagnifier();
14552     }
14553     else if (IS_DROPPABLE(element) ||
14554              IS_THROWABLE(element))     /* can be collected and dropped */
14555     {
14556       int i;
14557
14558       if (collect_count == 0)
14559         player->inventory_infinite_element = element;
14560       else
14561         for (i = 0; i < collect_count; i++)
14562           if (player->inventory_size < MAX_INVENTORY_SIZE)
14563             player->inventory_element[player->inventory_size++] = element;
14564
14565       DrawGameDoorValues();
14566     }
14567     else if (collect_count > 0)
14568     {
14569       local_player->gems_still_needed -= collect_count;
14570       if (local_player->gems_still_needed < 0)
14571         local_player->gems_still_needed = 0;
14572
14573 #if 1
14574       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14575
14576       DisplayGameControlValues();
14577 #else
14578       DrawGameValue_Emeralds(local_player->gems_still_needed);
14579 #endif
14580     }
14581
14582     RaiseScoreElement(element);
14583     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14584
14585     if (is_player)
14586       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14587                                           player->index_bit, dig_side);
14588
14589     if (mode == DF_SNAP)
14590     {
14591 #if USE_NEW_SNAP_DELAY
14592       if (level.block_snap_field)
14593         setFieldForSnapping(x, y, element, move_direction);
14594       else
14595         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14596 #else
14597       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14598 #endif
14599
14600       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14601                                           player->index_bit, dig_side);
14602     }
14603   }
14604   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14605   {
14606     if (mode == DF_SNAP && element != EL_BD_ROCK)
14607       return MP_NO_ACTION;
14608
14609     if (CAN_FALL(element) && dy)
14610       return MP_NO_ACTION;
14611
14612     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14613         !(element == EL_SPRING && level.use_spring_bug))
14614       return MP_NO_ACTION;
14615
14616     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14617         ((move_direction & MV_VERTICAL &&
14618           ((element_info[element].move_pattern & MV_LEFT &&
14619             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14620            (element_info[element].move_pattern & MV_RIGHT &&
14621             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14622          (move_direction & MV_HORIZONTAL &&
14623           ((element_info[element].move_pattern & MV_UP &&
14624             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14625            (element_info[element].move_pattern & MV_DOWN &&
14626             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14627       return MP_NO_ACTION;
14628
14629     /* do not push elements already moving away faster than player */
14630     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14631         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14632       return MP_NO_ACTION;
14633
14634     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14635     {
14636       if (player->push_delay_value == -1 || !player_was_pushing)
14637         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14638     }
14639     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14640     {
14641       if (player->push_delay_value == -1)
14642         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14643     }
14644     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14645     {
14646       if (!player->is_pushing)
14647         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14648     }
14649
14650     player->is_pushing = TRUE;
14651     player->is_active = TRUE;
14652
14653     if (!(IN_LEV_FIELD(nextx, nexty) &&
14654           (IS_FREE(nextx, nexty) ||
14655            (IS_SB_ELEMENT(element) &&
14656             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14657            (IS_CUSTOM_ELEMENT(element) &&
14658             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14659       return MP_NO_ACTION;
14660
14661     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14662       return MP_NO_ACTION;
14663
14664     if (player->push_delay == -1)       /* new pushing; restart delay */
14665       player->push_delay = 0;
14666
14667     if (player->push_delay < player->push_delay_value &&
14668         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14669         element != EL_SPRING && element != EL_BALLOON)
14670     {
14671       /* make sure that there is no move delay before next try to push */
14672       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14673         player->move_delay = 0;
14674
14675       return MP_NO_ACTION;
14676     }
14677
14678     if (IS_CUSTOM_ELEMENT(element) &&
14679         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14680     {
14681       if (!DigFieldByCE(nextx, nexty, element))
14682         return MP_NO_ACTION;
14683     }
14684
14685     if (IS_SB_ELEMENT(element))
14686     {
14687       if (element == EL_SOKOBAN_FIELD_FULL)
14688       {
14689         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14690         local_player->sokobanfields_still_needed++;
14691       }
14692
14693       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14694       {
14695         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14696         local_player->sokobanfields_still_needed--;
14697       }
14698
14699       Feld[x][y] = EL_SOKOBAN_OBJECT;
14700
14701       if (Back[x][y] == Back[nextx][nexty])
14702         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14703       else if (Back[x][y] != 0)
14704         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14705                                     ACTION_EMPTYING);
14706       else
14707         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14708                                     ACTION_FILLING);
14709
14710       if (local_player->sokobanfields_still_needed == 0 &&
14711           game.emulation == EMU_SOKOBAN)
14712       {
14713         PlayerWins(player);
14714
14715         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14716       }
14717     }
14718     else
14719       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14720
14721     InitMovingField(x, y, move_direction);
14722     GfxAction[x][y] = ACTION_PUSHING;
14723
14724     if (mode == DF_SNAP)
14725       ContinueMoving(x, y);
14726     else
14727       MovPos[x][y] = (dx != 0 ? dx : dy);
14728
14729     Pushed[x][y] = TRUE;
14730     Pushed[nextx][nexty] = TRUE;
14731
14732     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14733       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14734     else
14735       player->push_delay_value = -1;    /* get new value later */
14736
14737     /* check for element change _after_ element has been pushed */
14738     if (game.use_change_when_pushing_bug)
14739     {
14740       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14741                                  player->index_bit, dig_side);
14742       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14743                                           player->index_bit, dig_side);
14744     }
14745   }
14746   else if (IS_SWITCHABLE(element))
14747   {
14748     if (PLAYER_SWITCHING(player, x, y))
14749     {
14750       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14751                                           player->index_bit, dig_side);
14752
14753       return MP_ACTION;
14754     }
14755
14756     player->is_switching = TRUE;
14757     player->switch_x = x;
14758     player->switch_y = y;
14759
14760     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14761
14762     if (element == EL_ROBOT_WHEEL)
14763     {
14764       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14765       ZX = x;
14766       ZY = y;
14767
14768       game.robot_wheel_active = TRUE;
14769
14770       TEST_DrawLevelField(x, y);
14771     }
14772     else if (element == EL_SP_TERMINAL)
14773     {
14774       int xx, yy;
14775
14776       SCAN_PLAYFIELD(xx, yy)
14777       {
14778         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14779           Bang(xx, yy);
14780         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14781           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14782       }
14783     }
14784     else if (IS_BELT_SWITCH(element))
14785     {
14786       ToggleBeltSwitch(x, y);
14787     }
14788     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14789              element == EL_SWITCHGATE_SWITCH_DOWN ||
14790              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14791              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14792     {
14793       ToggleSwitchgateSwitch(x, y);
14794     }
14795     else if (element == EL_LIGHT_SWITCH ||
14796              element == EL_LIGHT_SWITCH_ACTIVE)
14797     {
14798       ToggleLightSwitch(x, y);
14799     }
14800     else if (element == EL_TIMEGATE_SWITCH ||
14801              element == EL_DC_TIMEGATE_SWITCH)
14802     {
14803       ActivateTimegateSwitch(x, y);
14804     }
14805     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14806              element == EL_BALLOON_SWITCH_RIGHT ||
14807              element == EL_BALLOON_SWITCH_UP    ||
14808              element == EL_BALLOON_SWITCH_DOWN  ||
14809              element == EL_BALLOON_SWITCH_NONE  ||
14810              element == EL_BALLOON_SWITCH_ANY)
14811     {
14812       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14813                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14814                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14815                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14816                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14817                              move_direction);
14818     }
14819     else if (element == EL_LAMP)
14820     {
14821       Feld[x][y] = EL_LAMP_ACTIVE;
14822       local_player->lights_still_needed--;
14823
14824       ResetGfxAnimation(x, y);
14825       TEST_DrawLevelField(x, y);
14826     }
14827     else if (element == EL_TIME_ORB_FULL)
14828     {
14829       Feld[x][y] = EL_TIME_ORB_EMPTY;
14830
14831       if (level.time > 0 || level.use_time_orb_bug)
14832       {
14833         TimeLeft += level.time_orb_time;
14834
14835 #if 1
14836         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14837
14838         DisplayGameControlValues();
14839 #else
14840         DrawGameValue_Time(TimeLeft);
14841 #endif
14842       }
14843
14844       ResetGfxAnimation(x, y);
14845       TEST_DrawLevelField(x, y);
14846     }
14847     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14848              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14849     {
14850       int xx, yy;
14851
14852       game.ball_state = !game.ball_state;
14853
14854       SCAN_PLAYFIELD(xx, yy)
14855       {
14856         int e = Feld[xx][yy];
14857
14858         if (game.ball_state)
14859         {
14860           if (e == EL_EMC_MAGIC_BALL)
14861             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14862           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14863             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14864         }
14865         else
14866         {
14867           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14868             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14869           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14870             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14871         }
14872       }
14873     }
14874
14875     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14876                                         player->index_bit, dig_side);
14877
14878     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14879                                         player->index_bit, dig_side);
14880
14881     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14882                                         player->index_bit, dig_side);
14883
14884     return MP_ACTION;
14885   }
14886   else
14887   {
14888     if (!PLAYER_SWITCHING(player, x, y))
14889     {
14890       player->is_switching = TRUE;
14891       player->switch_x = x;
14892       player->switch_y = y;
14893
14894       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14895                                  player->index_bit, dig_side);
14896       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14897                                           player->index_bit, dig_side);
14898
14899       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14900                                  player->index_bit, dig_side);
14901       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14902                                           player->index_bit, dig_side);
14903     }
14904
14905     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14906                                player->index_bit, dig_side);
14907     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14908                                         player->index_bit, dig_side);
14909
14910     return MP_NO_ACTION;
14911   }
14912
14913   player->push_delay = -1;
14914
14915   if (is_player)                /* function can also be called by EL_PENGUIN */
14916   {
14917     if (Feld[x][y] != element)          /* really digged/collected something */
14918     {
14919       player->is_collecting = !player->is_digging;
14920       player->is_active = TRUE;
14921     }
14922   }
14923
14924   return MP_MOVING;
14925 }
14926
14927 static boolean DigFieldByCE(int x, int y, int digging_element)
14928 {
14929   int element = Feld[x][y];
14930
14931   if (!IS_FREE(x, y))
14932   {
14933     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14934                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14935                   ACTION_BREAKING);
14936
14937     /* no element can dig solid indestructible elements */
14938     if (IS_INDESTRUCTIBLE(element) &&
14939         !IS_DIGGABLE(element) &&
14940         !IS_COLLECTIBLE(element))
14941       return FALSE;
14942
14943     if (AmoebaNr[x][y] &&
14944         (element == EL_AMOEBA_FULL ||
14945          element == EL_BD_AMOEBA ||
14946          element == EL_AMOEBA_GROWING))
14947     {
14948       AmoebaCnt[AmoebaNr[x][y]]--;
14949       AmoebaCnt2[AmoebaNr[x][y]]--;
14950     }
14951
14952     if (IS_MOVING(x, y))
14953       RemoveMovingField(x, y);
14954     else
14955     {
14956       RemoveField(x, y);
14957       TEST_DrawLevelField(x, y);
14958     }
14959
14960     /* if digged element was about to explode, prevent the explosion */
14961     ExplodeField[x][y] = EX_TYPE_NONE;
14962
14963     PlayLevelSoundAction(x, y, action);
14964   }
14965
14966   Store[x][y] = EL_EMPTY;
14967
14968 #if 1
14969   /* this makes it possible to leave the removed element again */
14970   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14971     Store[x][y] = element;
14972 #else
14973   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14974   {
14975     int move_leave_element = element_info[digging_element].move_leave_element;
14976
14977     /* this makes it possible to leave the removed element again */
14978     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
14979                    element : move_leave_element);
14980   }
14981 #endif
14982
14983   return TRUE;
14984 }
14985
14986 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14987 {
14988   int jx = player->jx, jy = player->jy;
14989   int x = jx + dx, y = jy + dy;
14990   int snap_direction = (dx == -1 ? MV_LEFT  :
14991                         dx == +1 ? MV_RIGHT :
14992                         dy == -1 ? MV_UP    :
14993                         dy == +1 ? MV_DOWN  : MV_NONE);
14994   boolean can_continue_snapping = (level.continuous_snapping &&
14995                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14996
14997   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14998     return FALSE;
14999
15000   if (!player->active || !IN_LEV_FIELD(x, y))
15001     return FALSE;
15002
15003   if (dx && dy)
15004     return FALSE;
15005
15006   if (!dx && !dy)
15007   {
15008     if (player->MovPos == 0)
15009       player->is_pushing = FALSE;
15010
15011     player->is_snapping = FALSE;
15012
15013     if (player->MovPos == 0)
15014     {
15015       player->is_moving = FALSE;
15016       player->is_digging = FALSE;
15017       player->is_collecting = FALSE;
15018     }
15019
15020     return FALSE;
15021   }
15022
15023 #if USE_NEW_CONTINUOUS_SNAPPING
15024   /* prevent snapping with already pressed snap key when not allowed */
15025   if (player->is_snapping && !can_continue_snapping)
15026     return FALSE;
15027 #else
15028   if (player->is_snapping)
15029     return FALSE;
15030 #endif
15031
15032   player->MovDir = snap_direction;
15033
15034   if (player->MovPos == 0)
15035   {
15036     player->is_moving = FALSE;
15037     player->is_digging = FALSE;
15038     player->is_collecting = FALSE;
15039   }
15040
15041   player->is_dropping = FALSE;
15042   player->is_dropping_pressed = FALSE;
15043   player->drop_pressed_delay = 0;
15044
15045   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15046     return FALSE;
15047
15048   player->is_snapping = TRUE;
15049   player->is_active = TRUE;
15050
15051   if (player->MovPos == 0)
15052   {
15053     player->is_moving = FALSE;
15054     player->is_digging = FALSE;
15055     player->is_collecting = FALSE;
15056   }
15057
15058   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15059     TEST_DrawLevelField(player->last_jx, player->last_jy);
15060
15061   TEST_DrawLevelField(x, y);
15062
15063   return TRUE;
15064 }
15065
15066 static boolean DropElement(struct PlayerInfo *player)
15067 {
15068   int old_element, new_element;
15069   int dropx = player->jx, dropy = player->jy;
15070   int drop_direction = player->MovDir;
15071   int drop_side = drop_direction;
15072 #if 1
15073   int drop_element = get_next_dropped_element(player);
15074 #else
15075   int drop_element = (player->inventory_size > 0 ?
15076                       player->inventory_element[player->inventory_size - 1] :
15077                       player->inventory_infinite_element != EL_UNDEFINED ?
15078                       player->inventory_infinite_element :
15079                       player->dynabombs_left > 0 ?
15080                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15081                       EL_UNDEFINED);
15082 #endif
15083
15084   player->is_dropping_pressed = TRUE;
15085
15086   /* do not drop an element on top of another element; when holding drop key
15087      pressed without moving, dropped element must move away before the next
15088      element can be dropped (this is especially important if the next element
15089      is dynamite, which can be placed on background for historical reasons) */
15090   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15091     return MP_ACTION;
15092
15093   if (IS_THROWABLE(drop_element))
15094   {
15095     dropx += GET_DX_FROM_DIR(drop_direction);
15096     dropy += GET_DY_FROM_DIR(drop_direction);
15097
15098     if (!IN_LEV_FIELD(dropx, dropy))
15099       return FALSE;
15100   }
15101
15102   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15103   new_element = drop_element;           /* default: no change when dropping */
15104
15105   /* check if player is active, not moving and ready to drop */
15106   if (!player->active || player->MovPos || player->drop_delay > 0)
15107     return FALSE;
15108
15109   /* check if player has anything that can be dropped */
15110   if (new_element == EL_UNDEFINED)
15111     return FALSE;
15112
15113   /* check if drop key was pressed long enough for EM style dynamite */
15114   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15115     return FALSE;
15116
15117   /* check if anything can be dropped at the current position */
15118   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15119     return FALSE;
15120
15121   /* collected custom elements can only be dropped on empty fields */
15122   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15123     return FALSE;
15124
15125   if (old_element != EL_EMPTY)
15126     Back[dropx][dropy] = old_element;   /* store old element on this field */
15127
15128   ResetGfxAnimation(dropx, dropy);
15129   ResetRandomAnimationValue(dropx, dropy);
15130
15131   if (player->inventory_size > 0 ||
15132       player->inventory_infinite_element != EL_UNDEFINED)
15133   {
15134     if (player->inventory_size > 0)
15135     {
15136       player->inventory_size--;
15137
15138       DrawGameDoorValues();
15139
15140       if (new_element == EL_DYNAMITE)
15141         new_element = EL_DYNAMITE_ACTIVE;
15142       else if (new_element == EL_EM_DYNAMITE)
15143         new_element = EL_EM_DYNAMITE_ACTIVE;
15144       else if (new_element == EL_SP_DISK_RED)
15145         new_element = EL_SP_DISK_RED_ACTIVE;
15146     }
15147
15148     Feld[dropx][dropy] = new_element;
15149
15150     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15151       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15152                           el2img(Feld[dropx][dropy]), 0);
15153
15154     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15155
15156     /* needed if previous element just changed to "empty" in the last frame */
15157     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15158
15159     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15160                                player->index_bit, drop_side);
15161     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15162                                         CE_PLAYER_DROPS_X,
15163                                         player->index_bit, drop_side);
15164
15165     TestIfElementTouchesCustomElement(dropx, dropy);
15166   }
15167   else          /* player is dropping a dyna bomb */
15168   {
15169     player->dynabombs_left--;
15170
15171     Feld[dropx][dropy] = new_element;
15172
15173     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15174       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15175                           el2img(Feld[dropx][dropy]), 0);
15176
15177     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15178   }
15179
15180   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15181     InitField_WithBug1(dropx, dropy, FALSE);
15182
15183   new_element = Feld[dropx][dropy];     /* element might have changed */
15184
15185   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15186       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15187   {
15188     int move_direction, nextx, nexty;
15189
15190     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15191       MovDir[dropx][dropy] = drop_direction;
15192
15193     move_direction = MovDir[dropx][dropy];
15194     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15195     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15196
15197     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15198
15199 #if USE_FIX_IMPACT_COLLISION
15200     /* do not cause impact style collision by dropping elements that can fall */
15201     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15202 #else
15203     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15204 #endif
15205   }
15206
15207   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15208   player->is_dropping = TRUE;
15209
15210   player->drop_pressed_delay = 0;
15211   player->is_dropping_pressed = FALSE;
15212
15213   player->drop_x = dropx;
15214   player->drop_y = dropy;
15215
15216   return TRUE;
15217 }
15218
15219 /* ------------------------------------------------------------------------- */
15220 /* game sound playing functions                                              */
15221 /* ------------------------------------------------------------------------- */
15222
15223 static int *loop_sound_frame = NULL;
15224 static int *loop_sound_volume = NULL;
15225
15226 void InitPlayLevelSound()
15227 {
15228   int num_sounds = getSoundListSize();
15229
15230   checked_free(loop_sound_frame);
15231   checked_free(loop_sound_volume);
15232
15233   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15234   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15235 }
15236
15237 static void PlayLevelSound(int x, int y, int nr)
15238 {
15239   int sx = SCREENX(x), sy = SCREENY(y);
15240   int volume, stereo_position;
15241   int max_distance = 8;
15242   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15243
15244   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15245       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15246     return;
15247
15248   if (!IN_LEV_FIELD(x, y) ||
15249       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15250       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15251     return;
15252
15253   volume = SOUND_MAX_VOLUME;
15254
15255   if (!IN_SCR_FIELD(sx, sy))
15256   {
15257     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15258     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15259
15260     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15261   }
15262
15263   stereo_position = (SOUND_MAX_LEFT +
15264                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15265                      (SCR_FIELDX + 2 * max_distance));
15266
15267   if (IS_LOOP_SOUND(nr))
15268   {
15269     /* This assures that quieter loop sounds do not overwrite louder ones,
15270        while restarting sound volume comparison with each new game frame. */
15271
15272     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15273       return;
15274
15275     loop_sound_volume[nr] = volume;
15276     loop_sound_frame[nr] = FrameCounter;
15277   }
15278
15279   PlaySoundExt(nr, volume, stereo_position, type);
15280 }
15281
15282 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15283 {
15284   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15285                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15286                  y < LEVELY(BY1) ? LEVELY(BY1) :
15287                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15288                  sound_action);
15289 }
15290
15291 static void PlayLevelSoundAction(int x, int y, int action)
15292 {
15293   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15294 }
15295
15296 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15297 {
15298   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15299
15300   if (sound_effect != SND_UNDEFINED)
15301     PlayLevelSound(x, y, sound_effect);
15302 }
15303
15304 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15305                                               int action)
15306 {
15307   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15308
15309   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15310     PlayLevelSound(x, y, sound_effect);
15311 }
15312
15313 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15314 {
15315   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15316
15317   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15318     PlayLevelSound(x, y, sound_effect);
15319 }
15320
15321 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15322 {
15323   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15324
15325   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15326     StopSound(sound_effect);
15327 }
15328
15329 static void PlayLevelMusic()
15330 {
15331   if (levelset.music[level_nr] != MUS_UNDEFINED)
15332     PlayMusic(levelset.music[level_nr]);        /* from config file */
15333   else
15334     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15335 }
15336
15337 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15338 {
15339   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15340   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15341   int x = xx - 1 - offset;
15342   int y = yy - 1 - offset;
15343
15344   switch (sample)
15345   {
15346     case SAMPLE_blank:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15348       break;
15349
15350     case SAMPLE_roll:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15352       break;
15353
15354     case SAMPLE_stone:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15356       break;
15357
15358     case SAMPLE_nut:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15360       break;
15361
15362     case SAMPLE_crack:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15364       break;
15365
15366     case SAMPLE_bug:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15368       break;
15369
15370     case SAMPLE_tank:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15372       break;
15373
15374     case SAMPLE_android_clone:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15376       break;
15377
15378     case SAMPLE_android_move:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15380       break;
15381
15382     case SAMPLE_spring:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15384       break;
15385
15386     case SAMPLE_slurp:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15388       break;
15389
15390     case SAMPLE_eater:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15392       break;
15393
15394     case SAMPLE_eater_eat:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15396       break;
15397
15398     case SAMPLE_alien:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15400       break;
15401
15402     case SAMPLE_collect:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15404       break;
15405
15406     case SAMPLE_diamond:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15408       break;
15409
15410     case SAMPLE_squash:
15411       /* !!! CHECK THIS !!! */
15412 #if 1
15413       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15414 #else
15415       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15416 #endif
15417       break;
15418
15419     case SAMPLE_wonderfall:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15421       break;
15422
15423     case SAMPLE_drip:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15425       break;
15426
15427     case SAMPLE_push:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15429       break;
15430
15431     case SAMPLE_dirt:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15433       break;
15434
15435     case SAMPLE_acid:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15437       break;
15438
15439     case SAMPLE_ball:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15441       break;
15442
15443     case SAMPLE_grow:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15445       break;
15446
15447     case SAMPLE_wonder:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15449       break;
15450
15451     case SAMPLE_door:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15453       break;
15454
15455     case SAMPLE_exit_open:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15457       break;
15458
15459     case SAMPLE_exit_leave:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15461       break;
15462
15463     case SAMPLE_dynamite:
15464       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15465       break;
15466
15467     case SAMPLE_tick:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15469       break;
15470
15471     case SAMPLE_press:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15473       break;
15474
15475     case SAMPLE_wheel:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15477       break;
15478
15479     case SAMPLE_boom:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15481       break;
15482
15483     case SAMPLE_die:
15484       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15485       break;
15486
15487     case SAMPLE_time:
15488       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15489       break;
15490
15491     default:
15492       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15493       break;
15494   }
15495 }
15496
15497 #if 0
15498 void ChangeTime(int value)
15499 {
15500   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15501
15502   *time += value;
15503
15504   /* EMC game engine uses value from time counter of RND game engine */
15505   level.native_em_level->lev->time = *time;
15506
15507   DrawGameValue_Time(*time);
15508 }
15509
15510 void RaiseScore(int value)
15511 {
15512   /* EMC game engine and RND game engine have separate score counters */
15513   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15514                 &level.native_em_level->lev->score : &local_player->score);
15515
15516   *score += value;
15517
15518   DrawGameValue_Score(*score);
15519 }
15520 #endif
15521
15522 void RaiseScore(int value)
15523 {
15524   local_player->score += value;
15525
15526 #if 1
15527   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15528
15529   DisplayGameControlValues();
15530 #else
15531   DrawGameValue_Score(local_player->score);
15532 #endif
15533 }
15534
15535 void RaiseScoreElement(int element)
15536 {
15537   switch (element)
15538   {
15539     case EL_EMERALD:
15540     case EL_BD_DIAMOND:
15541     case EL_EMERALD_YELLOW:
15542     case EL_EMERALD_RED:
15543     case EL_EMERALD_PURPLE:
15544     case EL_SP_INFOTRON:
15545       RaiseScore(level.score[SC_EMERALD]);
15546       break;
15547     case EL_DIAMOND:
15548       RaiseScore(level.score[SC_DIAMOND]);
15549       break;
15550     case EL_CRYSTAL:
15551       RaiseScore(level.score[SC_CRYSTAL]);
15552       break;
15553     case EL_PEARL:
15554       RaiseScore(level.score[SC_PEARL]);
15555       break;
15556     case EL_BUG:
15557     case EL_BD_BUTTERFLY:
15558     case EL_SP_ELECTRON:
15559       RaiseScore(level.score[SC_BUG]);
15560       break;
15561     case EL_SPACESHIP:
15562     case EL_BD_FIREFLY:
15563     case EL_SP_SNIKSNAK:
15564       RaiseScore(level.score[SC_SPACESHIP]);
15565       break;
15566     case EL_YAMYAM:
15567     case EL_DARK_YAMYAM:
15568       RaiseScore(level.score[SC_YAMYAM]);
15569       break;
15570     case EL_ROBOT:
15571       RaiseScore(level.score[SC_ROBOT]);
15572       break;
15573     case EL_PACMAN:
15574       RaiseScore(level.score[SC_PACMAN]);
15575       break;
15576     case EL_NUT:
15577       RaiseScore(level.score[SC_NUT]);
15578       break;
15579     case EL_DYNAMITE:
15580     case EL_EM_DYNAMITE:
15581     case EL_SP_DISK_RED:
15582     case EL_DYNABOMB_INCREASE_NUMBER:
15583     case EL_DYNABOMB_INCREASE_SIZE:
15584     case EL_DYNABOMB_INCREASE_POWER:
15585       RaiseScore(level.score[SC_DYNAMITE]);
15586       break;
15587     case EL_SHIELD_NORMAL:
15588     case EL_SHIELD_DEADLY:
15589       RaiseScore(level.score[SC_SHIELD]);
15590       break;
15591     case EL_EXTRA_TIME:
15592       RaiseScore(level.extra_time_score);
15593       break;
15594     case EL_KEY_1:
15595     case EL_KEY_2:
15596     case EL_KEY_3:
15597     case EL_KEY_4:
15598     case EL_EM_KEY_1:
15599     case EL_EM_KEY_2:
15600     case EL_EM_KEY_3:
15601     case EL_EM_KEY_4:
15602     case EL_EMC_KEY_5:
15603     case EL_EMC_KEY_6:
15604     case EL_EMC_KEY_7:
15605     case EL_EMC_KEY_8:
15606     case EL_DC_KEY_WHITE:
15607       RaiseScore(level.score[SC_KEY]);
15608       break;
15609     default:
15610       RaiseScore(element_info[element].collect_score);
15611       break;
15612   }
15613 }
15614
15615 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15616 {
15617   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15618   {
15619 #if defined(NETWORK_AVALIABLE)
15620     if (options.network)
15621       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15622     else
15623 #endif
15624     {
15625       if (quick_quit)
15626       {
15627 #if 1
15628
15629 #if 1
15630         FadeSkipNextFadeIn();
15631 #else
15632         fading = fading_none;
15633 #endif
15634
15635 #else
15636         OpenDoor(DOOR_CLOSE_1);
15637 #endif
15638
15639         game_status = GAME_MODE_MAIN;
15640
15641 #if 1
15642         DrawAndFadeInMainMenu(REDRAW_FIELD);
15643 #else
15644         DrawMainMenu();
15645 #endif
15646       }
15647       else
15648       {
15649 #if 0
15650         FadeOut(REDRAW_FIELD);
15651 #endif
15652
15653         game_status = GAME_MODE_MAIN;
15654
15655         DrawAndFadeInMainMenu(REDRAW_FIELD);
15656       }
15657     }
15658   }
15659   else          /* continue playing the game */
15660   {
15661     if (tape.playing && tape.deactivate_display)
15662       TapeDeactivateDisplayOff(TRUE);
15663
15664     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15665
15666     if (tape.playing && tape.deactivate_display)
15667       TapeDeactivateDisplayOn();
15668   }
15669 }
15670
15671 void RequestQuitGame(boolean ask_if_really_quit)
15672 {
15673   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15674   boolean skip_request = AllPlayersGone || quick_quit;
15675
15676   RequestQuitGameExt(skip_request, quick_quit,
15677                      "Do you really want to quit the game ?");
15678 }
15679
15680
15681 /* ------------------------------------------------------------------------- */
15682 /* random generator functions                                                */
15683 /* ------------------------------------------------------------------------- */
15684
15685 unsigned int InitEngineRandom_RND(long seed)
15686 {
15687   game.num_random_calls = 0;
15688
15689 #if 0
15690   unsigned int rnd_seed = InitEngineRandom(seed);
15691
15692   printf("::: START RND: %d\n", rnd_seed);
15693
15694   return rnd_seed;
15695 #else
15696
15697   return InitEngineRandom(seed);
15698
15699 #endif
15700
15701 }
15702
15703 unsigned int RND(int max)
15704 {
15705   if (max > 0)
15706   {
15707     game.num_random_calls++;
15708
15709     return GetEngineRandom(max);
15710   }
15711
15712   return 0;
15713 }
15714
15715
15716 /* ------------------------------------------------------------------------- */
15717 /* game engine snapshot handling functions                                   */
15718 /* ------------------------------------------------------------------------- */
15719
15720 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15721
15722 struct EngineSnapshotInfo
15723 {
15724   /* runtime values for custom element collect score */
15725   int collect_score[NUM_CUSTOM_ELEMENTS];
15726
15727   /* runtime values for group element choice position */
15728   int choice_pos[NUM_GROUP_ELEMENTS];
15729
15730   /* runtime values for belt position animations */
15731   int belt_graphic[4 * NUM_BELT_PARTS];
15732   int belt_anim_mode[4 * NUM_BELT_PARTS];
15733 };
15734
15735 struct EngineSnapshotNodeInfo
15736 {
15737   void *buffer_orig;
15738   void *buffer_copy;
15739   int size;
15740 };
15741
15742 static struct EngineSnapshotInfo engine_snapshot_rnd;
15743 static ListNode *engine_snapshot_list = NULL;
15744 static char *snapshot_level_identifier = NULL;
15745 static int snapshot_level_nr = -1;
15746
15747 void FreeEngineSnapshot()
15748 {
15749   while (engine_snapshot_list != NULL)
15750     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15751                        checked_free);
15752
15753   setString(&snapshot_level_identifier, NULL);
15754   snapshot_level_nr = -1;
15755 }
15756
15757 static void SaveEngineSnapshotValues_RND()
15758 {
15759   static int belt_base_active_element[4] =
15760   {
15761     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15762     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15763     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15764     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15765   };
15766   int i, j;
15767
15768   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15769   {
15770     int element = EL_CUSTOM_START + i;
15771
15772     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15773   }
15774
15775   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15776   {
15777     int element = EL_GROUP_START + i;
15778
15779     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15780   }
15781
15782   for (i = 0; i < 4; i++)
15783   {
15784     for (j = 0; j < NUM_BELT_PARTS; j++)
15785     {
15786       int element = belt_base_active_element[i] + j;
15787       int graphic = el2img(element);
15788       int anim_mode = graphic_info[graphic].anim_mode;
15789
15790       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15791       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15792     }
15793   }
15794 }
15795
15796 static void LoadEngineSnapshotValues_RND()
15797 {
15798   unsigned long num_random_calls = game.num_random_calls;
15799   int i, j;
15800
15801   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15802   {
15803     int element = EL_CUSTOM_START + i;
15804
15805     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15806   }
15807
15808   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15809   {
15810     int element = EL_GROUP_START + i;
15811
15812     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15813   }
15814
15815   for (i = 0; i < 4; i++)
15816   {
15817     for (j = 0; j < NUM_BELT_PARTS; j++)
15818     {
15819       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15820       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15821
15822       graphic_info[graphic].anim_mode = anim_mode;
15823     }
15824   }
15825
15826   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15827   {
15828     InitRND(tape.random_seed);
15829     for (i = 0; i < num_random_calls; i++)
15830       RND(1);
15831   }
15832
15833   if (game.num_random_calls != num_random_calls)
15834   {
15835     Error(ERR_INFO, "number of random calls out of sync");
15836     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15837     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15838     Error(ERR_EXIT, "this should not happen -- please debug");
15839   }
15840 }
15841
15842 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15843 {
15844   struct EngineSnapshotNodeInfo *bi =
15845     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15846
15847   bi->buffer_orig = buffer;
15848   bi->buffer_copy = checked_malloc(size);
15849   bi->size = size;
15850
15851   memcpy(bi->buffer_copy, buffer, size);
15852
15853   addNodeToList(&engine_snapshot_list, NULL, bi);
15854 }
15855
15856 void SaveEngineSnapshot()
15857 {
15858   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15859
15860   if (level_editor_test_game)   /* do not save snapshots from editor */
15861     return;
15862
15863   /* copy some special values to a structure better suited for the snapshot */
15864
15865   SaveEngineSnapshotValues_RND();
15866   SaveEngineSnapshotValues_EM();
15867
15868   /* save values stored in special snapshot structure */
15869
15870   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15871   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15872
15873   /* save further RND engine values */
15874
15875   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15876   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15877   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15878
15879   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15880   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15881   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15882   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15883
15884   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15885   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15886   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15887   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15888   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15889
15890   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15891   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15892   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15893
15894   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15895
15896   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15897
15898   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15899   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15900
15901   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15902   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15903   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15904   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15905   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15906   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15907   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15908   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15909   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15910   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15911   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15912   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15913   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15914   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15915   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15916   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15917   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15918   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15919
15920   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15921   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15922
15923   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15924   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15925   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15926
15927   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15928   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15929
15930   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15931   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15932   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15933   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15934   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15935
15936   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15937   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15938
15939   /* save level identification information */
15940
15941   setString(&snapshot_level_identifier, leveldir_current->identifier);
15942   snapshot_level_nr = level_nr;
15943
15944 #if 0
15945   ListNode *node = engine_snapshot_list;
15946   int num_bytes = 0;
15947
15948   while (node != NULL)
15949   {
15950     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15951
15952     node = node->next;
15953   }
15954
15955   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15956 #endif
15957 }
15958
15959 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15960 {
15961   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15962 }
15963
15964 void LoadEngineSnapshot()
15965 {
15966   ListNode *node = engine_snapshot_list;
15967
15968   if (engine_snapshot_list == NULL)
15969     return;
15970
15971   while (node != NULL)
15972   {
15973     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15974
15975     node = node->next;
15976   }
15977
15978   /* restore special values from snapshot structure */
15979
15980   LoadEngineSnapshotValues_RND();
15981   LoadEngineSnapshotValues_EM();
15982 }
15983
15984 boolean CheckEngineSnapshot()
15985 {
15986   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15987           snapshot_level_nr == level_nr);
15988 }
15989
15990
15991 /* ---------- new game button stuff ---------------------------------------- */
15992
15993 /* graphic position values for game buttons */
15994 #define GAME_BUTTON_XSIZE       30
15995 #define GAME_BUTTON_YSIZE       30
15996 #define GAME_BUTTON_XPOS        5
15997 #define GAME_BUTTON_YPOS        215
15998 #define SOUND_BUTTON_XPOS       5
15999 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16000
16001 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16002 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16003 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16004 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16005 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16006 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16007
16008 static struct
16009 {
16010   int *x, *y;
16011   int gd_x, gd_y;
16012   int gadget_id;
16013   char *infotext;
16014 } gamebutton_info[NUM_GAME_BUTTONS] =
16015 {
16016 #if 1
16017   {
16018     &game.button.stop.x,        &game.button.stop.y,
16019     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16020     GAME_CTRL_ID_STOP,
16021     "stop game"
16022   },
16023   {
16024     &game.button.pause.x,       &game.button.pause.y,
16025     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16026     GAME_CTRL_ID_PAUSE,
16027     "pause game"
16028   },
16029   {
16030     &game.button.play.x,        &game.button.play.y,
16031     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16032     GAME_CTRL_ID_PLAY,
16033     "play game"
16034   },
16035   {
16036     &game.button.sound_music.x, &game.button.sound_music.y,
16037     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16038     SOUND_CTRL_ID_MUSIC,
16039     "background music on/off"
16040   },
16041   {
16042     &game.button.sound_loops.x, &game.button.sound_loops.y,
16043     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16044     SOUND_CTRL_ID_LOOPS,
16045     "sound loops on/off"
16046   },
16047   {
16048     &game.button.sound_simple.x,&game.button.sound_simple.y,
16049     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16050     SOUND_CTRL_ID_SIMPLE,
16051     "normal sounds on/off"
16052   }
16053 #else
16054   {
16055     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16056     GAME_CTRL_ID_STOP,
16057     "stop game"
16058   },
16059   {
16060     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16061     GAME_CTRL_ID_PAUSE,
16062     "pause game"
16063   },
16064   {
16065     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16066     GAME_CTRL_ID_PLAY,
16067     "play game"
16068   },
16069   {
16070     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16071     SOUND_CTRL_ID_MUSIC,
16072     "background music on/off"
16073   },
16074   {
16075     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16076     SOUND_CTRL_ID_LOOPS,
16077     "sound loops on/off"
16078   },
16079   {
16080     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16081     SOUND_CTRL_ID_SIMPLE,
16082     "normal sounds on/off"
16083   }
16084 #endif
16085 };
16086
16087 void CreateGameButtons()
16088 {
16089   int i;
16090
16091   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16092   {
16093     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16094     struct GadgetInfo *gi;
16095     int button_type;
16096     boolean checked;
16097     unsigned long event_mask;
16098     int x, y;
16099     int gd_xoffset, gd_yoffset;
16100     int gd_x1, gd_x2, gd_y1, gd_y2;
16101     int id = i;
16102
16103     x = DX + *gamebutton_info[i].x;
16104     y = DY + *gamebutton_info[i].y;
16105     gd_xoffset = gamebutton_info[i].gd_x;
16106     gd_yoffset = gamebutton_info[i].gd_y;
16107     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16108     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16109
16110     if (id == GAME_CTRL_ID_STOP ||
16111         id == GAME_CTRL_ID_PAUSE ||
16112         id == GAME_CTRL_ID_PLAY)
16113     {
16114       button_type = GD_TYPE_NORMAL_BUTTON;
16115       checked = FALSE;
16116       event_mask = GD_EVENT_RELEASED;
16117       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16118       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16119     }
16120     else
16121     {
16122       button_type = GD_TYPE_CHECK_BUTTON;
16123       checked =
16124         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16125          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16126          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16127       event_mask = GD_EVENT_PRESSED;
16128       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16129       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16130     }
16131
16132     gi = CreateGadget(GDI_CUSTOM_ID, id,
16133                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16134 #if 1
16135                       GDI_X, x,
16136                       GDI_Y, y,
16137 #else
16138                       GDI_X, DX + gd_xoffset,
16139                       GDI_Y, DY + gd_yoffset,
16140 #endif
16141                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16142                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16143                       GDI_TYPE, button_type,
16144                       GDI_STATE, GD_BUTTON_UNPRESSED,
16145                       GDI_CHECKED, checked,
16146                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16147                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16148                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16149                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16150                       GDI_DIRECT_DRAW, FALSE,
16151                       GDI_EVENT_MASK, event_mask,
16152                       GDI_CALLBACK_ACTION, HandleGameButtons,
16153                       GDI_END);
16154
16155     if (gi == NULL)
16156       Error(ERR_EXIT, "cannot create gadget");
16157
16158     game_gadget[id] = gi;
16159   }
16160 }
16161
16162 void FreeGameButtons()
16163 {
16164   int i;
16165
16166   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16167     FreeGadget(game_gadget[i]);
16168 }
16169
16170 static void MapGameButtons()
16171 {
16172   int i;
16173
16174   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16175     MapGadget(game_gadget[i]);
16176 }
16177
16178 void UnmapGameButtons()
16179 {
16180   int i;
16181
16182   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16183     UnmapGadget(game_gadget[i]);
16184 }
16185
16186 void RedrawGameButtons()
16187 {
16188   int i;
16189
16190   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16191     RedrawGadget(game_gadget[i]);
16192 }
16193
16194 static void HandleGameButtons(struct GadgetInfo *gi)
16195 {
16196   int id = gi->custom_id;
16197
16198   if (game_status != GAME_MODE_PLAYING)
16199     return;
16200
16201   switch (id)
16202   {
16203     case GAME_CTRL_ID_STOP:
16204       if (tape.playing)
16205         TapeStop();
16206       else
16207         RequestQuitGame(TRUE);
16208       break;
16209
16210     case GAME_CTRL_ID_PAUSE:
16211       if (options.network)
16212       {
16213 #if defined(NETWORK_AVALIABLE)
16214         if (tape.pausing)
16215           SendToServer_ContinuePlaying();
16216         else
16217           SendToServer_PausePlaying();
16218 #endif
16219       }
16220       else
16221         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16222       break;
16223
16224     case GAME_CTRL_ID_PLAY:
16225       if (tape.pausing)
16226       {
16227 #if defined(NETWORK_AVALIABLE)
16228         if (options.network)
16229           SendToServer_ContinuePlaying();
16230         else
16231 #endif
16232         {
16233           tape.pausing = FALSE;
16234           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16235         }
16236       }
16237       break;
16238
16239     case SOUND_CTRL_ID_MUSIC:
16240       if (setup.sound_music)
16241       { 
16242         setup.sound_music = FALSE;
16243         FadeMusic();
16244       }
16245       else if (audio.music_available)
16246       { 
16247         setup.sound = setup.sound_music = TRUE;
16248
16249         SetAudioMode(setup.sound);
16250
16251         PlayLevelMusic();
16252       }
16253       break;
16254
16255     case SOUND_CTRL_ID_LOOPS:
16256       if (setup.sound_loops)
16257         setup.sound_loops = FALSE;
16258       else if (audio.loops_available)
16259       {
16260         setup.sound = setup.sound_loops = TRUE;
16261         SetAudioMode(setup.sound);
16262       }
16263       break;
16264
16265     case SOUND_CTRL_ID_SIMPLE:
16266       if (setup.sound_simple)
16267         setup.sound_simple = FALSE;
16268       else if (audio.sound_available)
16269       {
16270         setup.sound = setup.sound_simple = TRUE;
16271         SetAudioMode(setup.sound);
16272       }
16273       break;
16274
16275     default:
16276       break;
16277   }
16278 }