rnd-20100313-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2225               level.native_sp_level->game_sp->time_played :
2226               level.time == 0 ? TimePlayed : TimeLeft);
2227   int score = (local_player->LevelSolved ?
2228                local_player->LevelSolved_CountingScore :
2229                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230                level.native_em_level->lev->score :
2231                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232                level.native_sp_level->game_sp->score :
2233                local_player->score);
2234   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235               level.native_em_level->lev->required :
2236               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237               level.native_sp_level->game_sp->infotrons_still_needed :
2238               local_player->gems_still_needed);
2239   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2240                      level.native_em_level->lev->required > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2242                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2243                      local_player->gems_still_needed > 0 ||
2244                      local_player->sokobanfields_still_needed > 0 ||
2245                      local_player->lights_still_needed > 0);
2246
2247   UpdatePlayfieldElementCount();
2248
2249   /* update game panel control values */
2250
2251   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2252   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2253
2254   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2255   for (i = 0; i < MAX_NUM_KEYS; i++)
2256     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2257   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2258   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2259
2260   if (game.centered_player_nr == -1)
2261   {
2262     for (i = 0; i < MAX_PLAYERS; i++)
2263     {
2264       /* only one player in Supaplex game engine */
2265       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2266         break;
2267
2268       for (k = 0; k < MAX_NUM_KEYS; k++)
2269       {
2270         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2271         {
2272           if (level.native_em_level->ply[i]->keys & (1 << k))
2273             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2274               get_key_element_from_nr(k);
2275         }
2276         else if (stored_player[i].key[k])
2277           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278             get_key_element_from_nr(k);
2279       }
2280
2281       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2282         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2283           level.native_em_level->ply[i]->dynamite;
2284       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2285         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286           level.native_sp_level->game_sp->red_disk_count;
2287       else
2288         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289           stored_player[i].inventory_size;
2290
2291       if (stored_player[i].num_white_keys > 0)
2292         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2293           EL_DC_KEY_WHITE;
2294
2295       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2296         stored_player[i].num_white_keys;
2297     }
2298   }
2299   else
2300   {
2301     int player_nr = game.centered_player_nr;
2302
2303     for (k = 0; k < MAX_NUM_KEYS; k++)
2304     {
2305       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2306       {
2307         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2308           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309             get_key_element_from_nr(k);
2310       }
2311       else if (stored_player[player_nr].key[k])
2312         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313           get_key_element_from_nr(k);
2314     }
2315
2316     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2317       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318         level.native_em_level->ply[player_nr]->dynamite;
2319     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         level.native_sp_level->game_sp->red_disk_count;
2322     else
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         stored_player[player_nr].inventory_size;
2325
2326     if (stored_player[player_nr].num_white_keys > 0)
2327       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2328
2329     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2330       stored_player[player_nr].num_white_keys;
2331   }
2332
2333   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2334   {
2335     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2336       get_inventory_element_from_pos(local_player, i);
2337     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2338       get_inventory_element_from_pos(local_player, -i - 1);
2339   }
2340
2341   game_panel_controls[GAME_PANEL_SCORE].value = score;
2342   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2343
2344   game_panel_controls[GAME_PANEL_TIME].value = time;
2345
2346   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2347   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2348   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2349
2350   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2351     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2352      EL_EMPTY);
2353   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2354     local_player->shield_normal_time_left;
2355   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2356     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2357      EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2359     local_player->shield_deadly_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EXIT].value =
2362     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2363
2364   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2365     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2368      EL_EMC_MAGIC_BALL_SWITCH);
2369
2370   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2371     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2373     game.light_time_left;
2374
2375   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2376     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2378     game.timegate_time_left;
2379
2380   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2381     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2382
2383   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2384     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2385   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2386     game.lenses_time_left;
2387
2388   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2389     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2391     game.magnify_time_left;
2392
2393   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2394     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2395      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2396      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2397      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2398      EL_BALLOON_SWITCH_NONE);
2399
2400   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2401     local_player->dynabomb_count;
2402   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2403     local_player->dynabomb_size;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2405     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2406
2407   game_panel_controls[GAME_PANEL_PENGUINS].value =
2408     local_player->friends_still_needed;
2409
2410   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2411     local_player->sokobanfields_still_needed;
2412   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2413     local_player->sokobanfields_still_needed;
2414
2415   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2416     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2417
2418   for (i = 0; i < NUM_BELTS; i++)
2419   {
2420     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2421       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2422        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2423     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2424       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2425   }
2426
2427   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2428     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2430     game.magic_wall_time_left;
2431
2432 #if USE_PLAYER_GRAVITY
2433   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2434     local_player->gravity;
2435 #else
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2437 #endif
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   /* update game panel control frames */
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508   }
2509 }
2510
2511 void DisplayGameControlValues()
2512 {
2513   boolean redraw_panel = FALSE;
2514   int i;
2515
2516   for (i = 0; game_panel_controls[i].nr != -1; i++)
2517   {
2518     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2519
2520     if (PANEL_DEACTIVATED(gpc->pos))
2521       continue;
2522
2523     if (gpc->value == gpc->last_value &&
2524         gpc->frame == gpc->last_frame)
2525       continue;
2526
2527     redraw_panel = TRUE;
2528   }
2529
2530   if (!redraw_panel)
2531     return;
2532
2533   /* copy default game door content to main double buffer */
2534   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2535              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2536
2537   /* redraw game control buttons */
2538 #if 1
2539   RedrawGameButtons();
2540 #else
2541   UnmapGameButtons();
2542   MapGameButtons();
2543 #endif
2544
2545   game_status = GAME_MODE_PSEUDO_PANEL;
2546
2547 #if 1
2548   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2549 #else
2550   for (i = 0; game_panel_controls[i].nr != -1; i++)
2551 #endif
2552   {
2553 #if 1
2554     int nr = game_panel_order[i].nr;
2555     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2556 #else
2557     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2558     int nr = gpc->nr;
2559 #endif
2560     struct TextPosInfo *pos = gpc->pos;
2561     int type = gpc->type;
2562     int value = gpc->value;
2563     int frame = gpc->frame;
2564 #if 0
2565     int last_value = gpc->last_value;
2566     int last_frame = gpc->last_frame;
2567 #endif
2568     int size = pos->size;
2569     int font = pos->font;
2570     boolean draw_masked = pos->draw_masked;
2571     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2572
2573     if (PANEL_DEACTIVATED(pos))
2574       continue;
2575
2576 #if 0
2577     if (value == last_value && frame == last_frame)
2578       continue;
2579 #endif
2580
2581     gpc->last_value = value;
2582     gpc->last_frame = frame;
2583
2584 #if 0
2585     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2586 #endif
2587
2588     if (type == TYPE_INTEGER)
2589     {
2590       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2591           nr == GAME_PANEL_TIME)
2592       {
2593         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2594
2595         if (use_dynamic_size)           /* use dynamic number of digits */
2596         {
2597           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2598           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2599           int size2 = size1 + 1;
2600           int font1 = pos->font;
2601           int font2 = pos->font_alt;
2602
2603           size = (value < value_change ? size1 : size2);
2604           font = (value < value_change ? font1 : font2);
2605
2606 #if 0
2607           /* clear background if value just changed its size (dynamic digits) */
2608           if ((last_value < value_change) != (value < value_change))
2609           {
2610             int width1 = size1 * getFontWidth(font1);
2611             int width2 = size2 * getFontWidth(font2);
2612             int max_width = MAX(width1, width2);
2613             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2614
2615             pos->width = max_width;
2616
2617             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2618                                        max_width, max_height);
2619           }
2620 #endif
2621         }
2622       }
2623
2624 #if 1
2625       /* correct text size if "digits" is zero or less */
2626       if (size <= 0)
2627         size = strlen(int2str(value, size));
2628
2629       /* dynamically correct text alignment */
2630       pos->width = size * getFontWidth(font);
2631 #endif
2632
2633       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2634                   int2str(value, size), font, mask_mode);
2635     }
2636     else if (type == TYPE_ELEMENT)
2637     {
2638       int element, graphic;
2639       Bitmap *src_bitmap;
2640       int src_x, src_y;
2641       int width, height;
2642       int dst_x = PANEL_XPOS(pos);
2643       int dst_y = PANEL_YPOS(pos);
2644
2645 #if 1
2646       if (value != EL_UNDEFINED && value != EL_EMPTY)
2647       {
2648         element = value;
2649         graphic = el2panelimg(value);
2650
2651         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2652
2653 #if 1
2654         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2655           size = TILESIZE;
2656 #endif
2657
2658         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2659                               &src_x, &src_y);
2660
2661         width  = graphic_info[graphic].width  * size / TILESIZE;
2662         height = graphic_info[graphic].height * size / TILESIZE;
2663
2664         if (draw_masked)
2665         {
2666           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2667                         dst_x - src_x, dst_y - src_y);
2668           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2669                            dst_x, dst_y);
2670         }
2671         else
2672         {
2673           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2674                      dst_x, dst_y);
2675         }
2676       }
2677 #else
2678       if (value == EL_UNDEFINED || value == EL_EMPTY)
2679       {
2680         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2681         graphic = el2panelimg(element);
2682
2683         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2684         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2685         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2686       }
2687       else
2688       {
2689         element = value;
2690         graphic = el2panelimg(value);
2691
2692         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2693       }
2694
2695       width  = graphic_info[graphic].width  * size / TILESIZE;
2696       height = graphic_info[graphic].height * size / TILESIZE;
2697
2698       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2699 #endif
2700     }
2701     else if (type == TYPE_STRING)
2702     {
2703       boolean active = (value != 0);
2704       char *state_normal = "off";
2705       char *state_active = "on";
2706       char *state = (active ? state_active : state_normal);
2707       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2709                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2710                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2711
2712       if (nr == GAME_PANEL_GRAVITY_STATE)
2713       {
2714         int font1 = pos->font;          /* (used for normal state) */
2715         int font2 = pos->font_alt;      /* (used for active state) */
2716 #if 0
2717         int size1 = strlen(state_normal);
2718         int size2 = strlen(state_active);
2719         int width1 = size1 * getFontWidth(font1);
2720         int width2 = size2 * getFontWidth(font2);
2721         int max_width = MAX(width1, width2);
2722         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2723
2724         pos->width = max_width;
2725
2726         /* clear background for values that may have changed its size */
2727         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2728                                    max_width, max_height);
2729 #endif
2730
2731         font = (active ? font2 : font1);
2732       }
2733
2734       if (s != NULL)
2735       {
2736         char *s_cut;
2737
2738 #if 1
2739         if (size <= 0)
2740         {
2741           /* don't truncate output if "chars" is zero or less */
2742           size = strlen(s);
2743
2744           /* dynamically correct text alignment */
2745           pos->width = size * getFontWidth(font);
2746         }
2747 #endif
2748
2749         s_cut = getStringCopyN(s, size);
2750
2751         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2752                     s_cut, font, mask_mode);
2753
2754         free(s_cut);
2755       }
2756     }
2757
2758     redraw_mask |= REDRAW_DOOR_1;
2759   }
2760
2761   game_status = GAME_MODE_PLAYING;
2762 }
2763
2764 void UpdateAndDisplayGameControlValues()
2765 {
2766   if (tape.warp_forward)
2767     return;
2768
2769   UpdateGameControlValues();
2770   DisplayGameControlValues();
2771 }
2772
2773 void DrawGameValue_Emeralds(int value)
2774 {
2775   struct TextPosInfo *pos = &game.panel.gems;
2776 #if 1
2777   int font_nr = pos->font;
2778 #else
2779   int font_nr = FONT_TEXT_2;
2780 #endif
2781   int font_width = getFontWidth(font_nr);
2782   int chars = pos->size;
2783
2784 #if 1
2785   return;       /* !!! USE NEW STUFF !!! */
2786 #endif
2787
2788   if (PANEL_DEACTIVATED(pos))
2789     return;
2790
2791   pos->width = chars * font_width;
2792
2793   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2794 }
2795
2796 void DrawGameValue_Dynamite(int value)
2797 {
2798   struct TextPosInfo *pos = &game.panel.inventory_count;
2799 #if 1
2800   int font_nr = pos->font;
2801 #else
2802   int font_nr = FONT_TEXT_2;
2803 #endif
2804   int font_width = getFontWidth(font_nr);
2805   int chars = pos->size;
2806
2807 #if 1
2808   return;       /* !!! USE NEW STUFF !!! */
2809 #endif
2810
2811   if (PANEL_DEACTIVATED(pos))
2812     return;
2813
2814   pos->width = chars * font_width;
2815
2816   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2817 }
2818
2819 void DrawGameValue_Score(int value)
2820 {
2821   struct TextPosInfo *pos = &game.panel.score;
2822 #if 1
2823   int font_nr = pos->font;
2824 #else
2825   int font_nr = FONT_TEXT_2;
2826 #endif
2827   int font_width = getFontWidth(font_nr);
2828   int chars = pos->size;
2829
2830 #if 1
2831   return;       /* !!! USE NEW STUFF !!! */
2832 #endif
2833
2834   if (PANEL_DEACTIVATED(pos))
2835     return;
2836
2837   pos->width = chars * font_width;
2838
2839   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2840 }
2841
2842 void DrawGameValue_Time(int value)
2843 {
2844   struct TextPosInfo *pos = &game.panel.time;
2845   static int last_value = -1;
2846   int chars1 = 3;
2847   int chars2 = 4;
2848   int chars = pos->size;
2849 #if 1
2850   int font1_nr = pos->font;
2851   int font2_nr = pos->font_alt;
2852 #else
2853   int font1_nr = FONT_TEXT_2;
2854   int font2_nr = FONT_TEXT_1;
2855 #endif
2856   int font_nr = font1_nr;
2857   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2858
2859 #if 1
2860   return;       /* !!! USE NEW STUFF !!! */
2861 #endif
2862
2863   if (PANEL_DEACTIVATED(pos))
2864     return;
2865
2866   if (use_dynamic_chars)                /* use dynamic number of chars */
2867   {
2868     chars   = (value < 1000 ? chars1   : chars2);
2869     font_nr = (value < 1000 ? font1_nr : font2_nr);
2870   }
2871
2872   /* clear background if value just changed its size (dynamic chars only) */
2873   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2874   {
2875     int width1 = chars1 * getFontWidth(font1_nr);
2876     int width2 = chars2 * getFontWidth(font2_nr);
2877     int max_width = MAX(width1, width2);
2878     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2879
2880     pos->width = max_width;
2881
2882     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2883                                max_width, max_height);
2884   }
2885
2886   pos->width = chars * getFontWidth(font_nr);
2887
2888   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2889
2890   last_value = value;
2891 }
2892
2893 void DrawGameValue_Level(int value)
2894 {
2895   struct TextPosInfo *pos = &game.panel.level_number;
2896   int chars1 = 2;
2897   int chars2 = 3;
2898   int chars = pos->size;
2899 #if 1
2900   int font1_nr = pos->font;
2901   int font2_nr = pos->font_alt;
2902 #else
2903   int font1_nr = FONT_TEXT_2;
2904   int font2_nr = FONT_TEXT_1;
2905 #endif
2906   int font_nr = font1_nr;
2907   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2908
2909 #if 1
2910   return;       /* !!! USE NEW STUFF !!! */
2911 #endif
2912
2913   if (PANEL_DEACTIVATED(pos))
2914     return;
2915
2916   if (use_dynamic_chars)                /* use dynamic number of chars */
2917   {
2918     chars   = (level_nr < 100 ? chars1   : chars2);
2919     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2920   }
2921
2922   pos->width = chars * getFontWidth(font_nr);
2923
2924   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2925 }
2926
2927 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2928 {
2929 #if 0
2930   struct TextPosInfo *pos = &game.panel.keys;
2931 #endif
2932 #if 0
2933   int base_key_graphic = EL_KEY_1;
2934 #endif
2935   int i;
2936
2937 #if 1
2938   return;       /* !!! USE NEW STUFF !!! */
2939 #endif
2940
2941 #if 0
2942   if (PANEL_DEACTIVATED(pos))
2943     return;
2944 #endif
2945
2946 #if 0
2947   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2948     base_key_graphic = EL_EM_KEY_1;
2949 #endif
2950
2951 #if 0
2952   pos->width = 4 * MINI_TILEX;
2953 #endif
2954
2955 #if 1
2956   for (i = 0; i < MAX_NUM_KEYS; i++)
2957 #else
2958   /* currently only 4 of 8 possible keys are displayed */
2959   for (i = 0; i < STD_NUM_KEYS; i++)
2960 #endif
2961   {
2962 #if 1
2963     struct TextPosInfo *pos = &game.panel.key[i];
2964 #endif
2965     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2966     int src_y = DOOR_GFX_PAGEY1 + 123;
2967 #if 1
2968     int dst_x = PANEL_XPOS(pos);
2969     int dst_y = PANEL_YPOS(pos);
2970 #else
2971     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2972     int dst_y = PANEL_YPOS(pos);
2973 #endif
2974
2975 #if 1
2976     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2977                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2978                    EL_KEY_1) + i;
2979     int graphic = el2edimg(element);
2980 #endif
2981
2982 #if 1
2983     if (PANEL_DEACTIVATED(pos))
2984       continue;
2985 #endif
2986
2987 #if 0
2988     /* masked blit with tiles from half-size scaled bitmap does not work yet
2989        (no mask bitmap created for these sizes after loading and scaling) --
2990        solution: load without creating mask, scale, then create final mask */
2991
2992     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2993                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2994
2995     if (key[i])
2996     {
2997 #if 0
2998       int graphic = el2edimg(base_key_graphic + i);
2999 #endif
3000       Bitmap *src_bitmap;
3001       int src_x, src_y;
3002
3003       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3004
3005       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3006                     dst_x - src_x, dst_y - src_y);
3007       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3008                        dst_x, dst_y);
3009     }
3010 #else
3011 #if 1
3012     if (key[i])
3013       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3014     else
3015       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3016                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3017 #else
3018     if (key[i])
3019       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3020     else
3021       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3022                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3023 #endif
3024 #endif
3025   }
3026 }
3027
3028 #else
3029
3030 void DrawGameValue_Emeralds(int value)
3031 {
3032   int font_nr = FONT_TEXT_2;
3033   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3034
3035   if (PANEL_DEACTIVATED(game.panel.gems))
3036     return;
3037
3038   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3039 }
3040
3041 void DrawGameValue_Dynamite(int value)
3042 {
3043   int font_nr = FONT_TEXT_2;
3044   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3045
3046   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3047     return;
3048
3049   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3050 }
3051
3052 void DrawGameValue_Score(int value)
3053 {
3054   int font_nr = FONT_TEXT_2;
3055   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3056
3057   if (PANEL_DEACTIVATED(game.panel.score))
3058     return;
3059
3060   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3061 }
3062
3063 void DrawGameValue_Time(int value)
3064 {
3065   int font1_nr = FONT_TEXT_2;
3066 #if 1
3067   int font2_nr = FONT_TEXT_1;
3068 #else
3069   int font2_nr = FONT_LEVEL_NUMBER;
3070 #endif
3071   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3072   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3073
3074   if (PANEL_DEACTIVATED(game.panel.time))
3075     return;
3076
3077   /* clear background if value just changed its size */
3078   if (value == 999 || value == 1000)
3079     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3080
3081   if (value < 1000)
3082     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3083   else
3084     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3085 }
3086
3087 void DrawGameValue_Level(int value)
3088 {
3089   int font1_nr = FONT_TEXT_2;
3090 #if 1
3091   int font2_nr = FONT_TEXT_1;
3092 #else
3093   int font2_nr = FONT_LEVEL_NUMBER;
3094 #endif
3095
3096   if (PANEL_DEACTIVATED(game.panel.level))
3097     return;
3098
3099   if (level_nr < 100)
3100     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3101   else
3102     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3103 }
3104
3105 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3106 {
3107   int base_key_graphic = EL_KEY_1;
3108   int i;
3109
3110   if (PANEL_DEACTIVATED(game.panel.keys))
3111     return;
3112
3113   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3114     base_key_graphic = EL_EM_KEY_1;
3115
3116   /* currently only 4 of 8 possible keys are displayed */
3117   for (i = 0; i < STD_NUM_KEYS; i++)
3118   {
3119     int x = XX_KEYS + i * MINI_TILEX;
3120     int y = YY_KEYS;
3121
3122     if (key[i])
3123       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3124     else
3125       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3126                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3127   }
3128 }
3129
3130 #endif
3131
3132 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3133                        int key_bits)
3134 {
3135   int key[MAX_NUM_KEYS];
3136   int i;
3137
3138   /* prevent EM engine from updating time/score values parallel to GameWon() */
3139   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3140       local_player->LevelSolved)
3141     return;
3142
3143   for (i = 0; i < MAX_NUM_KEYS; i++)
3144     key[i] = key_bits & (1 << i);
3145
3146   DrawGameValue_Level(level_nr);
3147
3148   DrawGameValue_Emeralds(emeralds);
3149   DrawGameValue_Dynamite(dynamite);
3150   DrawGameValue_Score(score);
3151   DrawGameValue_Time(time);
3152
3153   DrawGameValue_Keys(key);
3154 }
3155
3156 void UpdateGameDoorValues()
3157 {
3158   UpdateGameControlValues();
3159 }
3160
3161 void DrawGameDoorValues()
3162 {
3163   DisplayGameControlValues();
3164 }
3165
3166 void DrawGameDoorValues_OLD()
3167 {
3168   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3169   int dynamite_value = 0;
3170   int score_value = (local_player->LevelSolved ? local_player->score_final :
3171                      local_player->score);
3172   int gems_value = local_player->gems_still_needed;
3173   int key_bits = 0;
3174   int i, j;
3175
3176   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3177   {
3178     DrawGameDoorValues_EM();
3179
3180     return;
3181   }
3182
3183   if (game.centered_player_nr == -1)
3184   {
3185     for (i = 0; i < MAX_PLAYERS; i++)
3186     {
3187       for (j = 0; j < MAX_NUM_KEYS; j++)
3188         if (stored_player[i].key[j])
3189           key_bits |= (1 << j);
3190
3191       dynamite_value += stored_player[i].inventory_size;
3192     }
3193   }
3194   else
3195   {
3196     int player_nr = game.centered_player_nr;
3197
3198     for (i = 0; i < MAX_NUM_KEYS; i++)
3199       if (stored_player[player_nr].key[i])
3200         key_bits |= (1 << i);
3201
3202     dynamite_value = stored_player[player_nr].inventory_size;
3203   }
3204
3205   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3206                     key_bits);
3207 }
3208
3209
3210 /*
3211   =============================================================================
3212   InitGameEngine()
3213   -----------------------------------------------------------------------------
3214   initialize game engine due to level / tape version number
3215   =============================================================================
3216 */
3217
3218 static void InitGameEngine()
3219 {
3220   int i, j, k, l, x, y;
3221
3222   /* set game engine from tape file when re-playing, else from level file */
3223   game.engine_version = (tape.playing ? tape.engine_version :
3224                          level.game_version);
3225
3226   /* ---------------------------------------------------------------------- */
3227   /* set flags for bugs and changes according to active game engine version */
3228   /* ---------------------------------------------------------------------- */
3229
3230   /*
3231     Summary of bugfix/change:
3232     Fixed handling for custom elements that change when pushed by the player.
3233
3234     Fixed/changed in version:
3235     3.1.0
3236
3237     Description:
3238     Before 3.1.0, custom elements that "change when pushing" changed directly
3239     after the player started pushing them (until then handled in "DigField()").
3240     Since 3.1.0, these custom elements are not changed until the "pushing"
3241     move of the element is finished (now handled in "ContinueMoving()").
3242
3243     Affected levels/tapes:
3244     The first condition is generally needed for all levels/tapes before version
3245     3.1.0, which might use the old behaviour before it was changed; known tapes
3246     that are affected are some tapes from the level set "Walpurgis Gardens" by
3247     Jamie Cullen.
3248     The second condition is an exception from the above case and is needed for
3249     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3250     above (including some development versions of 3.1.0), but before it was
3251     known that this change would break tapes like the above and was fixed in
3252     3.1.1, so that the changed behaviour was active although the engine version
3253     while recording maybe was before 3.1.0. There is at least one tape that is
3254     affected by this exception, which is the tape for the one-level set "Bug
3255     Machine" by Juergen Bonhagen.
3256   */
3257
3258   game.use_change_when_pushing_bug =
3259     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3260      !(tape.playing &&
3261        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3262        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3263
3264   /*
3265     Summary of bugfix/change:
3266     Fixed handling for blocking the field the player leaves when moving.
3267
3268     Fixed/changed in version:
3269     3.1.1
3270
3271     Description:
3272     Before 3.1.1, when "block last field when moving" was enabled, the field
3273     the player is leaving when moving was blocked for the time of the move,
3274     and was directly unblocked afterwards. This resulted in the last field
3275     being blocked for exactly one less than the number of frames of one player
3276     move. Additionally, even when blocking was disabled, the last field was
3277     blocked for exactly one frame.
3278     Since 3.1.1, due to changes in player movement handling, the last field
3279     is not blocked at all when blocking is disabled. When blocking is enabled,
3280     the last field is blocked for exactly the number of frames of one player
3281     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3282     last field is blocked for exactly one more than the number of frames of
3283     one player move.
3284
3285     Affected levels/tapes:
3286     (!!! yet to be determined -- probably many !!!)
3287   */
3288
3289   game.use_block_last_field_bug =
3290     (game.engine_version < VERSION_IDENT(3,1,1,0));
3291
3292   /*
3293     Summary of bugfix/change:
3294     Changed behaviour of CE changes with multiple changes per single frame.
3295
3296     Fixed/changed in version:
3297     3.2.0-6
3298
3299     Description:
3300     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3301     This resulted in race conditions where CEs seem to behave strange in some
3302     situations (where triggered CE changes were just skipped because there was
3303     already a CE change on that tile in the playfield in that engine frame).
3304     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3305     (The number of changes per frame must be limited in any case, because else
3306     it is easily possible to define CE changes that would result in an infinite
3307     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3308     should be set large enough so that it would only be reached in cases where
3309     the corresponding CE change conditions run into a loop. Therefore, it seems
3310     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3311     maximal number of change pages for custom elements.)
3312
3313     Affected levels/tapes:
3314     Probably many.
3315   */
3316
3317 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3318   game.max_num_changes_per_frame = 1;
3319 #else
3320   game.max_num_changes_per_frame =
3321     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3322 #endif
3323
3324   /* ---------------------------------------------------------------------- */
3325
3326   /* default scan direction: scan playfield from top/left to bottom/right */
3327   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3328
3329   /* dynamically adjust element properties according to game engine version */
3330   InitElementPropertiesEngine(game.engine_version);
3331
3332 #if 0
3333   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3334   printf("          tape version == %06d [%s] [file: %06d]\n",
3335          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3336          tape.file_version);
3337   printf("       => game.engine_version == %06d\n", game.engine_version);
3338 #endif
3339
3340   /* ---------- initialize player's initial move delay --------------------- */
3341
3342   /* dynamically adjust player properties according to level information */
3343   for (i = 0; i < MAX_PLAYERS; i++)
3344     game.initial_move_delay_value[i] =
3345       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3346
3347   /* dynamically adjust player properties according to game engine version */
3348   for (i = 0; i < MAX_PLAYERS; i++)
3349     game.initial_move_delay[i] =
3350       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3351        game.initial_move_delay_value[i] : 0);
3352
3353   /* ---------- initialize player's initial push delay --------------------- */
3354
3355   /* dynamically adjust player properties according to game engine version */
3356   game.initial_push_delay_value =
3357     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3358
3359   /* ---------- initialize changing elements ------------------------------- */
3360
3361   /* initialize changing elements information */
3362   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3363   {
3364     struct ElementInfo *ei = &element_info[i];
3365
3366     /* this pointer might have been changed in the level editor */
3367     ei->change = &ei->change_page[0];
3368
3369     if (!IS_CUSTOM_ELEMENT(i))
3370     {
3371       ei->change->target_element = EL_EMPTY_SPACE;
3372       ei->change->delay_fixed = 0;
3373       ei->change->delay_random = 0;
3374       ei->change->delay_frames = 1;
3375     }
3376
3377     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3378     {
3379       ei->has_change_event[j] = FALSE;
3380
3381       ei->event_page_nr[j] = 0;
3382       ei->event_page[j] = &ei->change_page[0];
3383     }
3384   }
3385
3386   /* add changing elements from pre-defined list */
3387   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3388   {
3389     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3390     struct ElementInfo *ei = &element_info[ch_delay->element];
3391
3392     ei->change->target_element       = ch_delay->target_element;
3393     ei->change->delay_fixed          = ch_delay->change_delay;
3394
3395     ei->change->pre_change_function  = ch_delay->pre_change_function;
3396     ei->change->change_function      = ch_delay->change_function;
3397     ei->change->post_change_function = ch_delay->post_change_function;
3398
3399     ei->change->can_change = TRUE;
3400     ei->change->can_change_or_has_action = TRUE;
3401
3402     ei->has_change_event[CE_DELAY] = TRUE;
3403
3404     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3405     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3406   }
3407
3408   /* ---------- initialize internal run-time variables --------------------- */
3409
3410   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3411   {
3412     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3413
3414     for (j = 0; j < ei->num_change_pages; j++)
3415     {
3416       ei->change_page[j].can_change_or_has_action =
3417         (ei->change_page[j].can_change |
3418          ei->change_page[j].has_action);
3419     }
3420   }
3421
3422   /* add change events from custom element configuration */
3423   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3424   {
3425     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3426
3427     for (j = 0; j < ei->num_change_pages; j++)
3428     {
3429       if (!ei->change_page[j].can_change_or_has_action)
3430         continue;
3431
3432       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3433       {
3434         /* only add event page for the first page found with this event */
3435         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3436         {
3437           ei->has_change_event[k] = TRUE;
3438
3439           ei->event_page_nr[k] = j;
3440           ei->event_page[k] = &ei->change_page[j];
3441         }
3442       }
3443     }
3444   }
3445
3446 #if 1
3447   /* ---------- initialize reference elements in change conditions --------- */
3448
3449   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3450   {
3451     int element = EL_CUSTOM_START + i;
3452     struct ElementInfo *ei = &element_info[element];
3453
3454     for (j = 0; j < ei->num_change_pages; j++)
3455     {
3456       int trigger_element = ei->change_page[j].initial_trigger_element;
3457
3458       if (trigger_element >= EL_PREV_CE_8 &&
3459           trigger_element <= EL_NEXT_CE_8)
3460         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3461
3462       ei->change_page[j].trigger_element = trigger_element;
3463     }
3464   }
3465 #endif
3466
3467   /* ---------- initialize run-time trigger player and element ------------- */
3468
3469   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3470   {
3471     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3472
3473     for (j = 0; j < ei->num_change_pages; j++)
3474     {
3475       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3476       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3477       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3478       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3479       ei->change_page[j].actual_trigger_ce_value = 0;
3480       ei->change_page[j].actual_trigger_ce_score = 0;
3481     }
3482   }
3483
3484   /* ---------- initialize trigger events ---------------------------------- */
3485
3486   /* initialize trigger events information */
3487   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3488     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3489       trigger_events[i][j] = FALSE;
3490
3491   /* add trigger events from element change event properties */
3492   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3493   {
3494     struct ElementInfo *ei = &element_info[i];
3495
3496     for (j = 0; j < ei->num_change_pages; j++)
3497     {
3498       if (!ei->change_page[j].can_change_or_has_action)
3499         continue;
3500
3501       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3502       {
3503         int trigger_element = ei->change_page[j].trigger_element;
3504
3505         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3506         {
3507           if (ei->change_page[j].has_event[k])
3508           {
3509             if (IS_GROUP_ELEMENT(trigger_element))
3510             {
3511               struct ElementGroupInfo *group =
3512                 element_info[trigger_element].group;
3513
3514               for (l = 0; l < group->num_elements_resolved; l++)
3515                 trigger_events[group->element_resolved[l]][k] = TRUE;
3516             }
3517             else if (trigger_element == EL_ANY_ELEMENT)
3518               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3519                 trigger_events[l][k] = TRUE;
3520             else
3521               trigger_events[trigger_element][k] = TRUE;
3522           }
3523         }
3524       }
3525     }
3526   }
3527
3528   /* ---------- initialize push delay -------------------------------------- */
3529
3530   /* initialize push delay values to default */
3531   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3532   {
3533     if (!IS_CUSTOM_ELEMENT(i))
3534     {
3535       /* set default push delay values (corrected since version 3.0.7-1) */
3536       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3537       {
3538         element_info[i].push_delay_fixed = 2;
3539         element_info[i].push_delay_random = 8;
3540       }
3541       else
3542       {
3543         element_info[i].push_delay_fixed = 8;
3544         element_info[i].push_delay_random = 8;
3545       }
3546     }
3547   }
3548
3549   /* set push delay value for certain elements from pre-defined list */
3550   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3551   {
3552     int e = push_delay_list[i].element;
3553
3554     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3555     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3556   }
3557
3558   /* set push delay value for Supaplex elements for newer engine versions */
3559   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3560   {
3561     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3562     {
3563       if (IS_SP_ELEMENT(i))
3564       {
3565         /* set SP push delay to just enough to push under a falling zonk */
3566         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3567
3568         element_info[i].push_delay_fixed  = delay;
3569         element_info[i].push_delay_random = 0;
3570       }
3571     }
3572   }
3573
3574   /* ---------- initialize move stepsize ----------------------------------- */
3575
3576   /* initialize move stepsize values to default */
3577   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578     if (!IS_CUSTOM_ELEMENT(i))
3579       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3580
3581   /* set move stepsize value for certain elements from pre-defined list */
3582   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3583   {
3584     int e = move_stepsize_list[i].element;
3585
3586     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3587   }
3588
3589   /* ---------- initialize collect score ----------------------------------- */
3590
3591   /* initialize collect score values for custom elements from initial value */
3592   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593     if (IS_CUSTOM_ELEMENT(i))
3594       element_info[i].collect_score = element_info[i].collect_score_initial;
3595
3596   /* ---------- initialize collect count ----------------------------------- */
3597
3598   /* initialize collect count values for non-custom elements */
3599   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3600     if (!IS_CUSTOM_ELEMENT(i))
3601       element_info[i].collect_count_initial = 0;
3602
3603   /* add collect count values for all elements from pre-defined list */
3604   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3605     element_info[collect_count_list[i].element].collect_count_initial =
3606       collect_count_list[i].count;
3607
3608   /* ---------- initialize access direction -------------------------------- */
3609
3610   /* initialize access direction values to default (access from every side) */
3611   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3612     if (!IS_CUSTOM_ELEMENT(i))
3613       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3614
3615   /* set access direction value for certain elements from pre-defined list */
3616   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3617     element_info[access_direction_list[i].element].access_direction =
3618       access_direction_list[i].direction;
3619
3620   /* ---------- initialize explosion content ------------------------------- */
3621   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3622   {
3623     if (IS_CUSTOM_ELEMENT(i))
3624       continue;
3625
3626     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3627     {
3628       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3629
3630       element_info[i].content.e[x][y] =
3631         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3632          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3633          i == EL_PLAYER_3 ? EL_EMERALD :
3634          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3635          i == EL_MOLE ? EL_EMERALD_RED :
3636          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3637          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3638          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3639          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3640          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3641          i == EL_WALL_EMERALD ? EL_EMERALD :
3642          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3643          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3644          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3645          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3646          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3647          i == EL_WALL_PEARL ? EL_PEARL :
3648          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3649          EL_EMPTY);
3650     }
3651   }
3652
3653   /* ---------- initialize recursion detection ------------------------------ */
3654   recursion_loop_depth = 0;
3655   recursion_loop_detected = FALSE;
3656   recursion_loop_element = EL_UNDEFINED;
3657
3658   /* ---------- initialize graphics engine ---------------------------------- */
3659   game.scroll_delay_value =
3660     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3661      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3662   game.scroll_delay_value =
3663     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3664 }
3665
3666 int get_num_special_action(int element, int action_first, int action_last)
3667 {
3668   int num_special_action = 0;
3669   int i, j;
3670
3671   for (i = action_first; i <= action_last; i++)
3672   {
3673     boolean found = FALSE;
3674
3675     for (j = 0; j < NUM_DIRECTIONS; j++)
3676       if (el_act_dir2img(element, i, j) !=
3677           el_act_dir2img(element, ACTION_DEFAULT, j))
3678         found = TRUE;
3679
3680     if (found)
3681       num_special_action++;
3682     else
3683       break;
3684   }
3685
3686   return num_special_action;
3687 }
3688
3689
3690 /*
3691   =============================================================================
3692   InitGame()
3693   -----------------------------------------------------------------------------
3694   initialize and start new game
3695   =============================================================================
3696 */
3697
3698 void InitGame()
3699 {
3700   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3701   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3702   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3703 #if 0
3704   boolean do_fading = (game_status == GAME_MODE_MAIN);
3705 #endif
3706 #if 1
3707   int initial_move_dir = MV_DOWN;
3708 #else
3709   int initial_move_dir = MV_NONE;
3710 #endif
3711   int i, j, x, y;
3712
3713   game_status = GAME_MODE_PLAYING;
3714
3715   InitGameEngine();
3716   InitGameControlValues();
3717
3718   /* don't play tapes over network */
3719   network_playing = (options.network && !tape.playing);
3720
3721   for (i = 0; i < MAX_PLAYERS; i++)
3722   {
3723     struct PlayerInfo *player = &stored_player[i];
3724
3725     player->index_nr = i;
3726     player->index_bit = (1 << i);
3727     player->element_nr = EL_PLAYER_1 + i;
3728
3729     player->present = FALSE;
3730     player->active = FALSE;
3731     player->mapped = FALSE;
3732
3733     player->killed = FALSE;
3734     player->reanimated = FALSE;
3735
3736     player->action = 0;
3737     player->effective_action = 0;
3738     player->programmed_action = 0;
3739
3740     player->score = 0;
3741     player->score_final = 0;
3742
3743     player->gems_still_needed = level.gems_needed;
3744     player->sokobanfields_still_needed = 0;
3745     player->lights_still_needed = 0;
3746     player->friends_still_needed = 0;
3747
3748     for (j = 0; j < MAX_NUM_KEYS; j++)
3749       player->key[j] = FALSE;
3750
3751     player->num_white_keys = 0;
3752
3753     player->dynabomb_count = 0;
3754     player->dynabomb_size = 1;
3755     player->dynabombs_left = 0;
3756     player->dynabomb_xl = FALSE;
3757
3758     player->MovDir = initial_move_dir;
3759     player->MovPos = 0;
3760     player->GfxPos = 0;
3761     player->GfxDir = initial_move_dir;
3762     player->GfxAction = ACTION_DEFAULT;
3763     player->Frame = 0;
3764     player->StepFrame = 0;
3765
3766     player->initial_element = player->element_nr;
3767     player->artwork_element =
3768       (level.use_artwork_element[i] ? level.artwork_element[i] :
3769        player->element_nr);
3770     player->use_murphy = FALSE;
3771
3772     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3773     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3774
3775     player->gravity = level.initial_player_gravity[i];
3776
3777     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3778
3779     player->actual_frame_counter = 0;
3780
3781     player->step_counter = 0;
3782
3783     player->last_move_dir = initial_move_dir;
3784
3785     player->is_active = FALSE;
3786
3787     player->is_waiting = FALSE;
3788     player->is_moving = FALSE;
3789     player->is_auto_moving = FALSE;
3790     player->is_digging = FALSE;
3791     player->is_snapping = FALSE;
3792     player->is_collecting = FALSE;
3793     player->is_pushing = FALSE;
3794     player->is_switching = FALSE;
3795     player->is_dropping = FALSE;
3796     player->is_dropping_pressed = FALSE;
3797
3798     player->is_bored = FALSE;
3799     player->is_sleeping = FALSE;
3800
3801     player->frame_counter_bored = -1;
3802     player->frame_counter_sleeping = -1;
3803
3804     player->anim_delay_counter = 0;
3805     player->post_delay_counter = 0;
3806
3807     player->dir_waiting = initial_move_dir;
3808     player->action_waiting = ACTION_DEFAULT;
3809     player->last_action_waiting = ACTION_DEFAULT;
3810     player->special_action_bored = ACTION_DEFAULT;
3811     player->special_action_sleeping = ACTION_DEFAULT;
3812
3813     player->switch_x = -1;
3814     player->switch_y = -1;
3815
3816     player->drop_x = -1;
3817     player->drop_y = -1;
3818
3819     player->show_envelope = 0;
3820
3821     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3822
3823     player->push_delay       = -1;      /* initialized when pushing starts */
3824     player->push_delay_value = game.initial_push_delay_value;
3825
3826     player->drop_delay = 0;
3827     player->drop_pressed_delay = 0;
3828
3829     player->last_jx = -1;
3830     player->last_jy = -1;
3831     player->jx = -1;
3832     player->jy = -1;
3833
3834     player->shield_normal_time_left = 0;
3835     player->shield_deadly_time_left = 0;
3836
3837     player->inventory_infinite_element = EL_UNDEFINED;
3838     player->inventory_size = 0;
3839
3840     if (level.use_initial_inventory[i])
3841     {
3842       for (j = 0; j < level.initial_inventory_size[i]; j++)
3843       {
3844         int element = level.initial_inventory_content[i][j];
3845         int collect_count = element_info[element].collect_count_initial;
3846         int k;
3847
3848         if (!IS_CUSTOM_ELEMENT(element))
3849           collect_count = 1;
3850
3851         if (collect_count == 0)
3852           player->inventory_infinite_element = element;
3853         else
3854           for (k = 0; k < collect_count; k++)
3855             if (player->inventory_size < MAX_INVENTORY_SIZE)
3856               player->inventory_element[player->inventory_size++] = element;
3857       }
3858     }
3859
3860     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3861     SnapField(player, 0, 0);
3862
3863     player->LevelSolved = FALSE;
3864     player->GameOver = FALSE;
3865
3866     player->LevelSolved_GameWon = FALSE;
3867     player->LevelSolved_GameEnd = FALSE;
3868     player->LevelSolved_PanelOff = FALSE;
3869     player->LevelSolved_SaveTape = FALSE;
3870     player->LevelSolved_SaveScore = FALSE;
3871     player->LevelSolved_CountingTime = 0;
3872     player->LevelSolved_CountingScore = 0;
3873
3874     map_player_action[i] = i;
3875   }
3876
3877   network_player_action_received = FALSE;
3878
3879 #if defined(NETWORK_AVALIABLE)
3880   /* initial null action */
3881   if (network_playing)
3882     SendToServer_MovePlayer(MV_NONE);
3883 #endif
3884
3885   ZX = ZY = -1;
3886   ExitX = ExitY = -1;
3887
3888   FrameCounter = 0;
3889   TimeFrames = 0;
3890   TimePlayed = 0;
3891   TimeLeft = level.time;
3892   TapeTime = 0;
3893
3894   ScreenMovDir = MV_NONE;
3895   ScreenMovPos = 0;
3896   ScreenGfxPos = 0;
3897
3898   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3899
3900   AllPlayersGone = FALSE;
3901
3902   game.yamyam_content_nr = 0;
3903   game.robot_wheel_active = FALSE;
3904   game.magic_wall_active = FALSE;
3905   game.magic_wall_time_left = 0;
3906   game.light_time_left = 0;
3907   game.timegate_time_left = 0;
3908   game.switchgate_pos = 0;
3909   game.wind_direction = level.wind_direction_initial;
3910
3911 #if !USE_PLAYER_GRAVITY
3912   game.gravity = FALSE;
3913   game.explosions_delayed = TRUE;
3914 #endif
3915
3916   game.lenses_time_left = 0;
3917   game.magnify_time_left = 0;
3918
3919   game.ball_state = level.ball_state_initial;
3920   game.ball_content_nr = 0;
3921
3922   game.envelope_active = FALSE;
3923
3924   /* set focus to local player for network games, else to all players */
3925   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3926   game.centered_player_nr_next = game.centered_player_nr;
3927   game.set_centered_player = FALSE;
3928
3929   if (network_playing && tape.recording)
3930   {
3931     /* store client dependent player focus when recording network games */
3932     tape.centered_player_nr_next = game.centered_player_nr_next;
3933     tape.set_centered_player = TRUE;
3934   }
3935
3936   for (i = 0; i < NUM_BELTS; i++)
3937   {
3938     game.belt_dir[i] = MV_NONE;
3939     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3940   }
3941
3942   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3943     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3944
3945   SCAN_PLAYFIELD(x, y)
3946   {
3947     Feld[x][y] = level.field[x][y];
3948     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3949     ChangeDelay[x][y] = 0;
3950     ChangePage[x][y] = -1;
3951 #if USE_NEW_CUSTOM_VALUE
3952     CustomValue[x][y] = 0;              /* initialized in InitField() */
3953 #endif
3954     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3955     AmoebaNr[x][y] = 0;
3956     WasJustMoving[x][y] = 0;
3957     WasJustFalling[x][y] = 0;
3958     CheckCollision[x][y] = 0;
3959     CheckImpact[x][y] = 0;
3960     Stop[x][y] = FALSE;
3961     Pushed[x][y] = FALSE;
3962
3963     ChangeCount[x][y] = 0;
3964     ChangeEvent[x][y] = -1;
3965
3966     ExplodePhase[x][y] = 0;
3967     ExplodeDelay[x][y] = 0;
3968     ExplodeField[x][y] = EX_TYPE_NONE;
3969
3970     RunnerVisit[x][y] = 0;
3971     PlayerVisit[x][y] = 0;
3972
3973     GfxFrame[x][y] = 0;
3974     GfxRandom[x][y] = INIT_GFX_RANDOM();
3975     GfxElement[x][y] = EL_UNDEFINED;
3976     GfxAction[x][y] = ACTION_DEFAULT;
3977     GfxDir[x][y] = MV_NONE;
3978     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3979   }
3980
3981   SCAN_PLAYFIELD(x, y)
3982   {
3983     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3984       emulate_bd = FALSE;
3985     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3986       emulate_sb = FALSE;
3987     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3988       emulate_sp = FALSE;
3989
3990     InitField(x, y, TRUE);
3991
3992     ResetGfxAnimation(x, y);
3993   }
3994
3995   InitBeltMovement();
3996
3997   for (i = 0; i < MAX_PLAYERS; i++)
3998   {
3999     struct PlayerInfo *player = &stored_player[i];
4000
4001     /* set number of special actions for bored and sleeping animation */
4002     player->num_special_action_bored =
4003       get_num_special_action(player->artwork_element,
4004                              ACTION_BORING_1, ACTION_BORING_LAST);
4005     player->num_special_action_sleeping =
4006       get_num_special_action(player->artwork_element,
4007                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4008   }
4009
4010   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4011                     emulate_sb ? EMU_SOKOBAN :
4012                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4013
4014 #if USE_NEW_ALL_SLIPPERY
4015   /* initialize type of slippery elements */
4016   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4017   {
4018     if (!IS_CUSTOM_ELEMENT(i))
4019     {
4020       /* default: elements slip down either to the left or right randomly */
4021       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4022
4023       /* SP style elements prefer to slip down on the left side */
4024       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4025         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4026
4027       /* BD style elements prefer to slip down on the left side */
4028       if (game.emulation == EMU_BOULDERDASH)
4029         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4030     }
4031   }
4032 #endif
4033
4034   /* initialize explosion and ignition delay */
4035   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4036   {
4037     if (!IS_CUSTOM_ELEMENT(i))
4038     {
4039       int num_phase = 8;
4040       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4041                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4042                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4043       int last_phase = (num_phase + 1) * delay;
4044       int half_phase = (num_phase / 2) * delay;
4045
4046       element_info[i].explosion_delay = last_phase - 1;
4047       element_info[i].ignition_delay = half_phase;
4048
4049       if (i == EL_BLACK_ORB)
4050         element_info[i].ignition_delay = 1;
4051     }
4052
4053 #if 0
4054     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4055       element_info[i].explosion_delay = 1;
4056
4057     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4058       element_info[i].ignition_delay = 1;
4059 #endif
4060   }
4061
4062   /* correct non-moving belts to start moving left */
4063   for (i = 0; i < NUM_BELTS; i++)
4064     if (game.belt_dir[i] == MV_NONE)
4065       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4066
4067 #if USE_NEW_PLAYER_ASSIGNMENTS
4068   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4069   /* choose default local player */
4070   local_player = &stored_player[0];
4071
4072   for (i = 0; i < MAX_PLAYERS; i++)
4073     stored_player[i].connected = FALSE;
4074
4075   local_player->connected = TRUE;
4076   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4077
4078   if (tape.playing)
4079   {
4080     /* try to guess locally connected team mode players (needed for correct
4081        assignment of player figures from level to locally playing players) */
4082
4083     for (i = 0; i < MAX_PLAYERS; i++)
4084       if (tape.player_participates[i])
4085         stored_player[i].connected = TRUE;
4086   }
4087   else if (setup.team_mode && !options.network)
4088   {
4089     /* try to guess locally connected team mode players (needed for correct
4090        assignment of player figures from level to locally playing players) */
4091
4092     for (i = 0; i < MAX_PLAYERS; i++)
4093       if (setup.input[i].use_joystick ||
4094           setup.input[i].key.left != KSYM_UNDEFINED)
4095         stored_player[i].connected = TRUE;
4096   }
4097
4098 #if 0
4099   for (i = 0; i < MAX_PLAYERS; i++)
4100     printf("::: player %d: %s\n", i,
4101            (stored_player[i].connected ? "connected" : "not connected"));
4102
4103   for (i = 0; i < MAX_PLAYERS; i++)
4104     printf("::: player %d: %s\n", i,
4105            (stored_player[i].present ? "present" : "not present"));
4106 #endif
4107
4108   /* check if any connected player was not found in playfield */
4109   for (i = 0; i < MAX_PLAYERS; i++)
4110   {
4111     struct PlayerInfo *player = &stored_player[i];
4112
4113     if (player->connected && !player->present)
4114     {
4115       struct PlayerInfo *field_player = NULL;
4116
4117 #if 0
4118       printf("::: looking for field player for player %d ...\n", i);
4119 #endif
4120
4121       /* assign first free player found that is present in the playfield */
4122
4123       /* first try: look for unmapped playfield player that is not connected */
4124       if (field_player == NULL)
4125         for (j = 0; j < MAX_PLAYERS; j++)
4126           if (stored_player[j].present &&
4127               !stored_player[j].mapped &&
4128               !stored_player[j].connected)
4129             field_player = &stored_player[j];
4130
4131       /* second try: look for *any* unmapped playfield player */
4132       if (field_player == NULL)
4133         for (j = 0; j < MAX_PLAYERS; j++)
4134           if (stored_player[j].present &&
4135               !stored_player[j].mapped)
4136             field_player = &stored_player[j];
4137
4138       if (field_player != NULL)
4139       {
4140         int jx = field_player->jx, jy = field_player->jy;
4141
4142 #if 0
4143         printf("::: found player figure %d\n", field_player->index_nr);
4144 #endif
4145
4146         player->present = FALSE;
4147         player->active = FALSE;
4148
4149         field_player->present = TRUE;
4150         field_player->active = TRUE;
4151
4152         /*
4153         player->initial_element = field_player->initial_element;
4154         player->artwork_element = field_player->artwork_element;
4155
4156         player->block_last_field       = field_player->block_last_field;
4157         player->block_delay_adjustment = field_player->block_delay_adjustment;
4158         */
4159
4160         StorePlayer[jx][jy] = field_player->element_nr;
4161
4162         field_player->jx = field_player->last_jx = jx;
4163         field_player->jy = field_player->last_jy = jy;
4164
4165         if (local_player == player)
4166           local_player = field_player;
4167
4168         map_player_action[field_player->index_nr] = i;
4169
4170         field_player->mapped = TRUE;
4171
4172 #if 0
4173         printf("::: map_player_action[%d] == %d\n",
4174                field_player->index_nr, i);
4175 #endif
4176       }
4177     }
4178
4179     if (player->connected && player->present)
4180       player->mapped = TRUE;
4181   }
4182
4183 #else
4184
4185   /* check if any connected player was not found in playfield */
4186   for (i = 0; i < MAX_PLAYERS; i++)
4187   {
4188     struct PlayerInfo *player = &stored_player[i];
4189
4190     if (player->connected && !player->present)
4191     {
4192       for (j = 0; j < MAX_PLAYERS; j++)
4193       {
4194         struct PlayerInfo *field_player = &stored_player[j];
4195         int jx = field_player->jx, jy = field_player->jy;
4196
4197         /* assign first free player found that is present in the playfield */
4198         if (field_player->present && !field_player->connected)
4199         {
4200           player->present = TRUE;
4201           player->active = TRUE;
4202
4203           field_player->present = FALSE;
4204           field_player->active = FALSE;
4205
4206           player->initial_element = field_player->initial_element;
4207           player->artwork_element = field_player->artwork_element;
4208
4209           player->block_last_field       = field_player->block_last_field;
4210           player->block_delay_adjustment = field_player->block_delay_adjustment;
4211
4212           StorePlayer[jx][jy] = player->element_nr;
4213
4214           player->jx = player->last_jx = jx;
4215           player->jy = player->last_jy = jy;
4216
4217           break;
4218         }
4219       }
4220     }
4221   }
4222 #endif
4223
4224 #if 0
4225   printf("::: local_player->present == %d\n", local_player->present);
4226 #endif
4227
4228   if (tape.playing)
4229   {
4230     /* when playing a tape, eliminate all players who do not participate */
4231
4232 #if USE_NEW_PLAYER_ASSIGNMENTS
4233     for (i = 0; i < MAX_PLAYERS; i++)
4234     {
4235       if (stored_player[i].active &&
4236           !tape.player_participates[map_player_action[i]])
4237       {
4238         struct PlayerInfo *player = &stored_player[i];
4239         int jx = player->jx, jy = player->jy;
4240
4241         player->active = FALSE;
4242         StorePlayer[jx][jy] = 0;
4243         Feld[jx][jy] = EL_EMPTY;
4244       }
4245     }
4246 #else
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248     {
4249       if (stored_player[i].active &&
4250           !tape.player_participates[i])
4251       {
4252         struct PlayerInfo *player = &stored_player[i];
4253         int jx = player->jx, jy = player->jy;
4254
4255         player->active = FALSE;
4256         StorePlayer[jx][jy] = 0;
4257         Feld[jx][jy] = EL_EMPTY;
4258       }
4259     }
4260 #endif
4261   }
4262   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4263   {
4264     /* when in single player mode, eliminate all but the first active player */
4265
4266     for (i = 0; i < MAX_PLAYERS; i++)
4267     {
4268       if (stored_player[i].active)
4269       {
4270         for (j = i + 1; j < MAX_PLAYERS; j++)
4271         {
4272           if (stored_player[j].active)
4273           {
4274             struct PlayerInfo *player = &stored_player[j];
4275             int jx = player->jx, jy = player->jy;
4276
4277             player->active = FALSE;
4278             player->present = FALSE;
4279
4280             StorePlayer[jx][jy] = 0;
4281             Feld[jx][jy] = EL_EMPTY;
4282           }
4283         }
4284       }
4285     }
4286   }
4287
4288   /* when recording the game, store which players take part in the game */
4289   if (tape.recording)
4290   {
4291 #if USE_NEW_PLAYER_ASSIGNMENTS
4292     for (i = 0; i < MAX_PLAYERS; i++)
4293       if (stored_player[i].connected)
4294         tape.player_participates[i] = TRUE;
4295 #else
4296     for (i = 0; i < MAX_PLAYERS; i++)
4297       if (stored_player[i].active)
4298         tape.player_participates[i] = TRUE;
4299 #endif
4300   }
4301
4302   if (options.debug)
4303   {
4304     for (i = 0; i < MAX_PLAYERS; i++)
4305     {
4306       struct PlayerInfo *player = &stored_player[i];
4307
4308       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4309              i+1,
4310              player->present,
4311              player->connected,
4312              player->active);
4313       if (local_player == player)
4314         printf("Player  %d is local player.\n", i+1);
4315     }
4316   }
4317
4318   if (BorderElement == EL_EMPTY)
4319   {
4320     SBX_Left = 0;
4321     SBX_Right = lev_fieldx - SCR_FIELDX;
4322     SBY_Upper = 0;
4323     SBY_Lower = lev_fieldy - SCR_FIELDY;
4324   }
4325   else
4326   {
4327     SBX_Left = -1;
4328     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4329     SBY_Upper = -1;
4330     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4331   }
4332
4333   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4334     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4335
4336   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4337     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4338
4339   /* if local player not found, look for custom element that might create
4340      the player (make some assumptions about the right custom element) */
4341   if (!local_player->present)
4342   {
4343     int start_x = 0, start_y = 0;
4344     int found_rating = 0;
4345     int found_element = EL_UNDEFINED;
4346     int player_nr = local_player->index_nr;
4347
4348     SCAN_PLAYFIELD(x, y)
4349     {
4350       int element = Feld[x][y];
4351       int content;
4352       int xx, yy;
4353       boolean is_player;
4354
4355       if (level.use_start_element[player_nr] &&
4356           level.start_element[player_nr] == element &&
4357           found_rating < 4)
4358       {
4359         start_x = x;
4360         start_y = y;
4361
4362         found_rating = 4;
4363         found_element = element;
4364       }
4365
4366       if (!IS_CUSTOM_ELEMENT(element))
4367         continue;
4368
4369       if (CAN_CHANGE(element))
4370       {
4371         for (i = 0; i < element_info[element].num_change_pages; i++)
4372         {
4373           /* check for player created from custom element as single target */
4374           content = element_info[element].change_page[i].target_element;
4375           is_player = ELEM_IS_PLAYER(content);
4376
4377           if (is_player && (found_rating < 3 ||
4378                             (found_rating == 3 && element < found_element)))
4379           {
4380             start_x = x;
4381             start_y = y;
4382
4383             found_rating = 3;
4384             found_element = element;
4385           }
4386         }
4387       }
4388
4389       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4390       {
4391         /* check for player created from custom element as explosion content */
4392         content = element_info[element].content.e[xx][yy];
4393         is_player = ELEM_IS_PLAYER(content);
4394
4395         if (is_player && (found_rating < 2 ||
4396                           (found_rating == 2 && element < found_element)))
4397         {
4398           start_x = x + xx - 1;
4399           start_y = y + yy - 1;
4400
4401           found_rating = 2;
4402           found_element = element;
4403         }
4404
4405         if (!CAN_CHANGE(element))
4406           continue;
4407
4408         for (i = 0; i < element_info[element].num_change_pages; i++)
4409         {
4410           /* check for player created from custom element as extended target */
4411           content =
4412             element_info[element].change_page[i].target_content.e[xx][yy];
4413
4414           is_player = ELEM_IS_PLAYER(content);
4415
4416           if (is_player && (found_rating < 1 ||
4417                             (found_rating == 1 && element < found_element)))
4418           {
4419             start_x = x + xx - 1;
4420             start_y = y + yy - 1;
4421
4422             found_rating = 1;
4423             found_element = element;
4424           }
4425         }
4426       }
4427     }
4428
4429     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4430                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4431                 start_x - MIDPOSX);
4432
4433     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4434                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4435                 start_y - MIDPOSY);
4436   }
4437   else
4438   {
4439     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4440                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4441                 local_player->jx - MIDPOSX);
4442
4443     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4444                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4445                 local_player->jy - MIDPOSY);
4446   }
4447
4448 #if 0
4449   /* do not use PLAYING mask for fading out from main screen */
4450   game_status = GAME_MODE_MAIN;
4451 #endif
4452
4453   StopAnimation();
4454
4455   if (!game.restart_level)
4456     CloseDoor(DOOR_CLOSE_1);
4457
4458 #if 1
4459   if (level_editor_test_game)
4460     FadeSkipNextFadeIn();
4461   else
4462     FadeSetEnterScreen();
4463 #else
4464   if (level_editor_test_game)
4465     fading = fading_none;
4466   else
4467     fading = menu.destination;
4468 #endif
4469
4470 #if 1
4471   FadeOut(REDRAW_FIELD);
4472 #else
4473   if (do_fading)
4474     FadeOut(REDRAW_FIELD);
4475 #endif
4476
4477 #if 0
4478   game_status = GAME_MODE_PLAYING;
4479 #endif
4480
4481   /* !!! FIX THIS (START) !!! */
4482   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4483   {
4484     InitGameEngine_EM();
4485
4486     /* blit playfield from scroll buffer to normal back buffer for fading in */
4487     BlitScreenToBitmap_EM(backbuffer);
4488   }
4489   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4490   {
4491     InitGameEngine_SP();
4492
4493     /* blit playfield from scroll buffer to normal back buffer for fading in */
4494     BlitScreenToBitmap_SP(backbuffer);
4495   }
4496   else
4497   {
4498     DrawLevel();
4499     DrawAllPlayers();
4500
4501     /* after drawing the level, correct some elements */
4502     if (game.timegate_time_left == 0)
4503       CloseAllOpenTimegates();
4504
4505     /* blit playfield from scroll buffer to normal back buffer for fading in */
4506     if (setup.soft_scrolling)
4507       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4508
4509     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4510   }
4511   /* !!! FIX THIS (END) !!! */
4512
4513 #if 1
4514   FadeIn(REDRAW_FIELD);
4515 #else
4516   if (do_fading)
4517     FadeIn(REDRAW_FIELD);
4518
4519   BackToFront();
4520 #endif
4521
4522   if (!game.restart_level)
4523   {
4524     /* copy default game door content to main double buffer */
4525     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4526                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4527   }
4528
4529   SetPanelBackground();
4530   SetDrawBackgroundMask(REDRAW_DOOR_1);
4531
4532 #if 1
4533   UpdateAndDisplayGameControlValues();
4534 #else
4535   UpdateGameDoorValues();
4536   DrawGameDoorValues();
4537 #endif
4538
4539   if (!game.restart_level)
4540   {
4541     UnmapGameButtons();
4542     UnmapTapeButtons();
4543     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4544     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4545     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4546     MapGameButtons();
4547     MapTapeButtons();
4548
4549     /* copy actual game door content to door double buffer for OpenDoor() */
4550     BlitBitmap(drawto, bitmap_db_door,
4551                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4552
4553     OpenDoor(DOOR_OPEN_ALL);
4554
4555     PlaySound(SND_GAME_STARTING);
4556
4557     if (setup.sound_music)
4558       PlayLevelMusic();
4559
4560     KeyboardAutoRepeatOffUnlessAutoplay();
4561
4562     if (options.debug)
4563     {
4564       for (i = 0; i < MAX_PLAYERS; i++)
4565         printf("Player %d %sactive.\n",
4566                i + 1, (stored_player[i].active ? "" : "not "));
4567     }
4568   }
4569
4570 #if 1
4571   UnmapAllGadgets();
4572
4573   MapGameButtons();
4574   MapTapeButtons();
4575 #endif
4576
4577   game.restart_level = FALSE;
4578 }
4579
4580 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4581 {
4582   /* this is used for non-R'n'D game engines to update certain engine values */
4583
4584   /* needed to determine if sounds are played within the visible screen area */
4585   scroll_x = actual_scroll_x;
4586   scroll_y = actual_scroll_y;
4587 }
4588
4589 void InitMovDir(int x, int y)
4590 {
4591   int i, element = Feld[x][y];
4592   static int xy[4][2] =
4593   {
4594     {  0, +1 },
4595     { +1,  0 },
4596     {  0, -1 },
4597     { -1,  0 }
4598   };
4599   static int direction[3][4] =
4600   {
4601     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4602     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4603     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4604   };
4605
4606   switch (element)
4607   {
4608     case EL_BUG_RIGHT:
4609     case EL_BUG_UP:
4610     case EL_BUG_LEFT:
4611     case EL_BUG_DOWN:
4612       Feld[x][y] = EL_BUG;
4613       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4614       break;
4615
4616     case EL_SPACESHIP_RIGHT:
4617     case EL_SPACESHIP_UP:
4618     case EL_SPACESHIP_LEFT:
4619     case EL_SPACESHIP_DOWN:
4620       Feld[x][y] = EL_SPACESHIP;
4621       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4622       break;
4623
4624     case EL_BD_BUTTERFLY_RIGHT:
4625     case EL_BD_BUTTERFLY_UP:
4626     case EL_BD_BUTTERFLY_LEFT:
4627     case EL_BD_BUTTERFLY_DOWN:
4628       Feld[x][y] = EL_BD_BUTTERFLY;
4629       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4630       break;
4631
4632     case EL_BD_FIREFLY_RIGHT:
4633     case EL_BD_FIREFLY_UP:
4634     case EL_BD_FIREFLY_LEFT:
4635     case EL_BD_FIREFLY_DOWN:
4636       Feld[x][y] = EL_BD_FIREFLY;
4637       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4638       break;
4639
4640     case EL_PACMAN_RIGHT:
4641     case EL_PACMAN_UP:
4642     case EL_PACMAN_LEFT:
4643     case EL_PACMAN_DOWN:
4644       Feld[x][y] = EL_PACMAN;
4645       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4646       break;
4647
4648     case EL_YAMYAM_LEFT:
4649     case EL_YAMYAM_RIGHT:
4650     case EL_YAMYAM_UP:
4651     case EL_YAMYAM_DOWN:
4652       Feld[x][y] = EL_YAMYAM;
4653       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4654       break;
4655
4656     case EL_SP_SNIKSNAK:
4657       MovDir[x][y] = MV_UP;
4658       break;
4659
4660     case EL_SP_ELECTRON:
4661       MovDir[x][y] = MV_LEFT;
4662       break;
4663
4664     case EL_MOLE_LEFT:
4665     case EL_MOLE_RIGHT:
4666     case EL_MOLE_UP:
4667     case EL_MOLE_DOWN:
4668       Feld[x][y] = EL_MOLE;
4669       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4670       break;
4671
4672     default:
4673       if (IS_CUSTOM_ELEMENT(element))
4674       {
4675         struct ElementInfo *ei = &element_info[element];
4676         int move_direction_initial = ei->move_direction_initial;
4677         int move_pattern = ei->move_pattern;
4678
4679         if (move_direction_initial == MV_START_PREVIOUS)
4680         {
4681           if (MovDir[x][y] != MV_NONE)
4682             return;
4683
4684           move_direction_initial = MV_START_AUTOMATIC;
4685         }
4686
4687         if (move_direction_initial == MV_START_RANDOM)
4688           MovDir[x][y] = 1 << RND(4);
4689         else if (move_direction_initial & MV_ANY_DIRECTION)
4690           MovDir[x][y] = move_direction_initial;
4691         else if (move_pattern == MV_ALL_DIRECTIONS ||
4692                  move_pattern == MV_TURNING_LEFT ||
4693                  move_pattern == MV_TURNING_RIGHT ||
4694                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4695                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4696                  move_pattern == MV_TURNING_RANDOM)
4697           MovDir[x][y] = 1 << RND(4);
4698         else if (move_pattern == MV_HORIZONTAL)
4699           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4700         else if (move_pattern == MV_VERTICAL)
4701           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4702         else if (move_pattern & MV_ANY_DIRECTION)
4703           MovDir[x][y] = element_info[element].move_pattern;
4704         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4705                  move_pattern == MV_ALONG_RIGHT_SIDE)
4706         {
4707           /* use random direction as default start direction */
4708           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4709             MovDir[x][y] = 1 << RND(4);
4710
4711           for (i = 0; i < NUM_DIRECTIONS; i++)
4712           {
4713             int x1 = x + xy[i][0];
4714             int y1 = y + xy[i][1];
4715
4716             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4717             {
4718               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4719                 MovDir[x][y] = direction[0][i];
4720               else
4721                 MovDir[x][y] = direction[1][i];
4722
4723               break;
4724             }
4725           }
4726         }                
4727       }
4728       else
4729       {
4730         MovDir[x][y] = 1 << RND(4);
4731
4732         if (element != EL_BUG &&
4733             element != EL_SPACESHIP &&
4734             element != EL_BD_BUTTERFLY &&
4735             element != EL_BD_FIREFLY)
4736           break;
4737
4738         for (i = 0; i < NUM_DIRECTIONS; i++)
4739         {
4740           int x1 = x + xy[i][0];
4741           int y1 = y + xy[i][1];
4742
4743           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4744           {
4745             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4746             {
4747               MovDir[x][y] = direction[0][i];
4748               break;
4749             }
4750             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4751                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4752             {
4753               MovDir[x][y] = direction[1][i];
4754               break;
4755             }
4756           }
4757         }
4758       }
4759       break;
4760   }
4761
4762   GfxDir[x][y] = MovDir[x][y];
4763 }
4764
4765 void InitAmoebaNr(int x, int y)
4766 {
4767   int i;
4768   int group_nr = AmoebeNachbarNr(x, y);
4769
4770   if (group_nr == 0)
4771   {
4772     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4773     {
4774       if (AmoebaCnt[i] == 0)
4775       {
4776         group_nr = i;
4777         break;
4778       }
4779     }
4780   }
4781
4782   AmoebaNr[x][y] = group_nr;
4783   AmoebaCnt[group_nr]++;
4784   AmoebaCnt2[group_nr]++;
4785 }
4786
4787 static void PlayerWins(struct PlayerInfo *player)
4788 {
4789   player->LevelSolved = TRUE;
4790   player->GameOver = TRUE;
4791
4792   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4793                          level.native_em_level->lev->score : player->score);
4794
4795   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4796   player->LevelSolved_CountingScore = player->score_final;
4797 }
4798
4799 void GameWon()
4800 {
4801   static int time, time_final;
4802   static int score, score_final;
4803   static int game_over_delay_1 = 0;
4804   static int game_over_delay_2 = 0;
4805   int game_over_delay_value_1 = 50;
4806   int game_over_delay_value_2 = 50;
4807
4808   if (!local_player->LevelSolved_GameWon)
4809   {
4810     int i;
4811
4812     /* do not start end game actions before the player stops moving (to exit) */
4813     if (local_player->MovPos)
4814       return;
4815
4816     local_player->LevelSolved_GameWon = TRUE;
4817     local_player->LevelSolved_SaveTape = tape.recording;
4818     local_player->LevelSolved_SaveScore = !tape.playing;
4819
4820     if (tape.auto_play)         /* tape might already be stopped here */
4821       tape.auto_play_level_solved = TRUE;
4822
4823 #if 1
4824     TapeStop();
4825 #endif
4826
4827     game_over_delay_1 = game_over_delay_value_1;
4828     game_over_delay_2 = game_over_delay_value_2;
4829
4830     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4831     score = score_final = local_player->score_final;
4832
4833     if (TimeLeft > 0)
4834     {
4835       time_final = 0;
4836       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4837     }
4838     else if (level.time == 0 && TimePlayed < 999)
4839     {
4840       time_final = 999;
4841       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4842     }
4843
4844     local_player->score_final = score_final;
4845
4846     if (level_editor_test_game)
4847     {
4848       time = time_final;
4849       score = score_final;
4850
4851 #if 1
4852       local_player->LevelSolved_CountingTime = time;
4853       local_player->LevelSolved_CountingScore = score;
4854
4855       game_panel_controls[GAME_PANEL_TIME].value = time;
4856       game_panel_controls[GAME_PANEL_SCORE].value = score;
4857
4858       DisplayGameControlValues();
4859 #else
4860       DrawGameValue_Time(time);
4861       DrawGameValue_Score(score);
4862 #endif
4863     }
4864
4865     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4866     {
4867       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4868       {
4869         /* close exit door after last player */
4870         if ((AllPlayersGone &&
4871              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4872               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4873               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4874             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4875             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4876         {
4877           int element = Feld[ExitX][ExitY];
4878
4879 #if 0
4880           if (element == EL_EM_EXIT_OPEN ||
4881               element == EL_EM_STEEL_EXIT_OPEN)
4882           {
4883             Bang(ExitX, ExitY);
4884           }
4885           else
4886 #endif
4887           {
4888             Feld[ExitX][ExitY] =
4889               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4890                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4891                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4892                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4893                EL_EM_STEEL_EXIT_CLOSING);
4894
4895             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4896           }
4897         }
4898
4899         /* player disappears */
4900         DrawLevelField(ExitX, ExitY);
4901       }
4902
4903       for (i = 0; i < MAX_PLAYERS; i++)
4904       {
4905         struct PlayerInfo *player = &stored_player[i];
4906
4907         if (player->present)
4908         {
4909           RemovePlayer(player);
4910
4911           /* player disappears */
4912           DrawLevelField(player->jx, player->jy);
4913         }
4914       }
4915     }
4916
4917     PlaySound(SND_GAME_WINNING);
4918   }
4919
4920   if (game_over_delay_1 > 0)
4921   {
4922     game_over_delay_1--;
4923
4924     return;
4925   }
4926
4927   if (time != time_final)
4928   {
4929     int time_to_go = ABS(time_final - time);
4930     int time_count_dir = (time < time_final ? +1 : -1);
4931     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4932
4933     time  += time_count_steps * time_count_dir;
4934     score += time_count_steps * level.score[SC_TIME_BONUS];
4935
4936 #if 1
4937     local_player->LevelSolved_CountingTime = time;
4938     local_player->LevelSolved_CountingScore = score;
4939
4940     game_panel_controls[GAME_PANEL_TIME].value = time;
4941     game_panel_controls[GAME_PANEL_SCORE].value = score;
4942
4943     DisplayGameControlValues();
4944 #else
4945     DrawGameValue_Time(time);
4946     DrawGameValue_Score(score);
4947 #endif
4948
4949     if (time == time_final)
4950       StopSound(SND_GAME_LEVELTIME_BONUS);
4951     else if (setup.sound_loops)
4952       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4953     else
4954       PlaySound(SND_GAME_LEVELTIME_BONUS);
4955
4956     return;
4957   }
4958
4959   local_player->LevelSolved_PanelOff = TRUE;
4960
4961   if (game_over_delay_2 > 0)
4962   {
4963     game_over_delay_2--;
4964
4965     return;
4966   }
4967
4968 #if 1
4969   GameEnd();
4970 #endif
4971 }
4972
4973 void GameEnd()
4974 {
4975   int hi_pos;
4976   boolean raise_level = FALSE;
4977
4978   local_player->LevelSolved_GameEnd = TRUE;
4979
4980   CloseDoor(DOOR_CLOSE_1);
4981
4982   if (local_player->LevelSolved_SaveTape)
4983   {
4984 #if 0
4985     TapeStop();
4986 #endif
4987
4988 #if 1
4989     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4990 #else
4991     SaveTape(tape.level_nr);            /* ask to save tape */
4992 #endif
4993   }
4994
4995   if (level_editor_test_game)
4996   {
4997     game_status = GAME_MODE_MAIN;
4998
4999 #if 1
5000     DrawAndFadeInMainMenu(REDRAW_FIELD);
5001 #else
5002     DrawMainMenu();
5003 #endif
5004
5005     return;
5006   }
5007
5008   if (!local_player->LevelSolved_SaveScore)
5009   {
5010 #if 1
5011     FadeOut(REDRAW_FIELD);
5012 #endif
5013
5014     game_status = GAME_MODE_MAIN;
5015
5016     DrawAndFadeInMainMenu(REDRAW_FIELD);
5017
5018     return;
5019   }
5020
5021   if (level_nr == leveldir_current->handicap_level)
5022   {
5023     leveldir_current->handicap_level++;
5024     SaveLevelSetup_SeriesInfo();
5025   }
5026
5027   if (level_nr < leveldir_current->last_level)
5028     raise_level = TRUE;                 /* advance to next level */
5029
5030   if ((hi_pos = NewHiScore()) >= 0) 
5031   {
5032     game_status = GAME_MODE_SCORES;
5033
5034     DrawHallOfFame(hi_pos);
5035
5036     if (raise_level)
5037     {
5038       level_nr++;
5039       TapeErase();
5040     }
5041   }
5042   else
5043   {
5044 #if 1
5045     FadeOut(REDRAW_FIELD);
5046 #endif
5047
5048     game_status = GAME_MODE_MAIN;
5049
5050     if (raise_level)
5051     {
5052       level_nr++;
5053       TapeErase();
5054     }
5055
5056     DrawAndFadeInMainMenu(REDRAW_FIELD);
5057   }
5058 }
5059
5060 int NewHiScore()
5061 {
5062   int k, l;
5063   int position = -1;
5064
5065   LoadScore(level_nr);
5066
5067   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5068       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5069     return -1;
5070
5071   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5072   {
5073     if (local_player->score_final > highscore[k].Score)
5074     {
5075       /* player has made it to the hall of fame */
5076
5077       if (k < MAX_SCORE_ENTRIES - 1)
5078       {
5079         int m = MAX_SCORE_ENTRIES - 1;
5080
5081 #ifdef ONE_PER_NAME
5082         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5083           if (strEqual(setup.player_name, highscore[l].Name))
5084             m = l;
5085         if (m == k)     /* player's new highscore overwrites his old one */
5086           goto put_into_list;
5087 #endif
5088
5089         for (l = m; l > k; l--)
5090         {
5091           strcpy(highscore[l].Name, highscore[l - 1].Name);
5092           highscore[l].Score = highscore[l - 1].Score;
5093         }
5094       }
5095
5096 #ifdef ONE_PER_NAME
5097       put_into_list:
5098 #endif
5099       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5100       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5101       highscore[k].Score = local_player->score_final; 
5102       position = k;
5103       break;
5104     }
5105
5106 #ifdef ONE_PER_NAME
5107     else if (!strncmp(setup.player_name, highscore[k].Name,
5108                       MAX_PLAYER_NAME_LEN))
5109       break;    /* player already there with a higher score */
5110 #endif
5111
5112   }
5113
5114   if (position >= 0) 
5115     SaveScore(level_nr);
5116
5117   return position;
5118 }
5119
5120 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5121 {
5122   int element = Feld[x][y];
5123   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5124   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5125   int horiz_move = (dx != 0);
5126   int sign = (horiz_move ? dx : dy);
5127   int step = sign * element_info[element].move_stepsize;
5128
5129   /* special values for move stepsize for spring and things on conveyor belt */
5130   if (horiz_move)
5131   {
5132     if (CAN_FALL(element) &&
5133         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5134       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5135     else if (element == EL_SPRING)
5136       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5137   }
5138
5139   return step;
5140 }
5141
5142 inline static int getElementMoveStepsize(int x, int y)
5143 {
5144   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5145 }
5146
5147 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5148 {
5149   if (player->GfxAction != action || player->GfxDir != dir)
5150   {
5151 #if 0
5152     printf("Player frame reset! (%d => %d, %d => %d)\n",
5153            player->GfxAction, action, player->GfxDir, dir);
5154 #endif
5155
5156     player->GfxAction = action;
5157     player->GfxDir = dir;
5158     player->Frame = 0;
5159     player->StepFrame = 0;
5160   }
5161 }
5162
5163 #if USE_GFX_RESET_GFX_ANIMATION
5164 static void ResetGfxFrame(int x, int y, boolean redraw)
5165 {
5166   int element = Feld[x][y];
5167   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5168   int last_gfx_frame = GfxFrame[x][y];
5169
5170   if (graphic_info[graphic].anim_global_sync)
5171     GfxFrame[x][y] = FrameCounter;
5172   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5173     GfxFrame[x][y] = CustomValue[x][y];
5174   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5175     GfxFrame[x][y] = element_info[element].collect_score;
5176   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5177     GfxFrame[x][y] = ChangeDelay[x][y];
5178
5179   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5180     DrawLevelGraphicAnimation(x, y, graphic);
5181 }
5182 #endif
5183
5184 static void ResetGfxAnimation(int x, int y)
5185 {
5186   GfxAction[x][y] = ACTION_DEFAULT;
5187   GfxDir[x][y] = MovDir[x][y];
5188   GfxFrame[x][y] = 0;
5189
5190 #if USE_GFX_RESET_GFX_ANIMATION
5191   ResetGfxFrame(x, y, FALSE);
5192 #endif
5193 }
5194
5195 static void ResetRandomAnimationValue(int x, int y)
5196 {
5197   GfxRandom[x][y] = INIT_GFX_RANDOM();
5198 }
5199
5200 void InitMovingField(int x, int y, int direction)
5201 {
5202   int element = Feld[x][y];
5203   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5204   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5205   int newx = x + dx;
5206   int newy = y + dy;
5207   boolean is_moving_before, is_moving_after;
5208 #if 0
5209   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5210 #endif
5211
5212   /* check if element was/is moving or being moved before/after mode change */
5213 #if 1
5214 #if 1
5215   is_moving_before = (WasJustMoving[x][y] != 0);
5216 #else
5217   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5218   is_moving_before = WasJustMoving[x][y];
5219 #endif
5220 #else
5221   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5222 #endif
5223   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5224
5225   /* reset animation only for moving elements which change direction of moving
5226      or which just started or stopped moving
5227      (else CEs with property "can move" / "not moving" are reset each frame) */
5228 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5229 #if 1
5230   if (is_moving_before != is_moving_after ||
5231       direction != MovDir[x][y])
5232     ResetGfxAnimation(x, y);
5233 #else
5234   if ((is_moving_before || is_moving_after) && !continues_moving)
5235     ResetGfxAnimation(x, y);
5236 #endif
5237 #else
5238   if (!continues_moving)
5239     ResetGfxAnimation(x, y);
5240 #endif
5241
5242   MovDir[x][y] = direction;
5243   GfxDir[x][y] = direction;
5244
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5246   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5247                      direction == MV_DOWN && CAN_FALL(element) ?
5248                      ACTION_FALLING : ACTION_MOVING);
5249 #else
5250   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5251                      ACTION_FALLING : ACTION_MOVING);
5252 #endif
5253
5254   /* this is needed for CEs with property "can move" / "not moving" */
5255
5256   if (is_moving_after)
5257   {
5258     if (Feld[newx][newy] == EL_EMPTY)
5259       Feld[newx][newy] = EL_BLOCKED;
5260
5261     MovDir[newx][newy] = MovDir[x][y];
5262
5263 #if USE_NEW_CUSTOM_VALUE
5264     CustomValue[newx][newy] = CustomValue[x][y];
5265 #endif
5266
5267     GfxFrame[newx][newy] = GfxFrame[x][y];
5268     GfxRandom[newx][newy] = GfxRandom[x][y];
5269     GfxAction[newx][newy] = GfxAction[x][y];
5270     GfxDir[newx][newy] = GfxDir[x][y];
5271   }
5272 }
5273
5274 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5275 {
5276   int direction = MovDir[x][y];
5277   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5278   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5279
5280   *goes_to_x = newx;
5281   *goes_to_y = newy;
5282 }
5283
5284 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5285 {
5286   int oldx = x, oldy = y;
5287   int direction = MovDir[x][y];
5288
5289   if (direction == MV_LEFT)
5290     oldx++;
5291   else if (direction == MV_RIGHT)
5292     oldx--;
5293   else if (direction == MV_UP)
5294     oldy++;
5295   else if (direction == MV_DOWN)
5296     oldy--;
5297
5298   *comes_from_x = oldx;
5299   *comes_from_y = oldy;
5300 }
5301
5302 int MovingOrBlocked2Element(int x, int y)
5303 {
5304   int element = Feld[x][y];
5305
5306   if (element == EL_BLOCKED)
5307   {
5308     int oldx, oldy;
5309
5310     Blocked2Moving(x, y, &oldx, &oldy);
5311     return Feld[oldx][oldy];
5312   }
5313   else
5314     return element;
5315 }
5316
5317 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5318 {
5319   /* like MovingOrBlocked2Element(), but if element is moving
5320      and (x,y) is the field the moving element is just leaving,
5321      return EL_BLOCKED instead of the element value */
5322   int element = Feld[x][y];
5323
5324   if (IS_MOVING(x, y))
5325   {
5326     if (element == EL_BLOCKED)
5327     {
5328       int oldx, oldy;
5329
5330       Blocked2Moving(x, y, &oldx, &oldy);
5331       return Feld[oldx][oldy];
5332     }
5333     else
5334       return EL_BLOCKED;
5335   }
5336   else
5337     return element;
5338 }
5339
5340 static void RemoveField(int x, int y)
5341 {
5342   Feld[x][y] = EL_EMPTY;
5343
5344   MovPos[x][y] = 0;
5345   MovDir[x][y] = 0;
5346   MovDelay[x][y] = 0;
5347
5348 #if USE_NEW_CUSTOM_VALUE
5349   CustomValue[x][y] = 0;
5350 #endif
5351
5352   AmoebaNr[x][y] = 0;
5353   ChangeDelay[x][y] = 0;
5354   ChangePage[x][y] = -1;
5355   Pushed[x][y] = FALSE;
5356
5357 #if 0
5358   ExplodeField[x][y] = EX_TYPE_NONE;
5359 #endif
5360
5361   GfxElement[x][y] = EL_UNDEFINED;
5362   GfxAction[x][y] = ACTION_DEFAULT;
5363   GfxDir[x][y] = MV_NONE;
5364 #if 0
5365   /* !!! this would prevent the removed tile from being redrawn !!! */
5366   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5367 #endif
5368 }
5369
5370 void RemoveMovingField(int x, int y)
5371 {
5372   int oldx = x, oldy = y, newx = x, newy = y;
5373   int element = Feld[x][y];
5374   int next_element = EL_UNDEFINED;
5375
5376   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5377     return;
5378
5379   if (IS_MOVING(x, y))
5380   {
5381     Moving2Blocked(x, y, &newx, &newy);
5382
5383     if (Feld[newx][newy] != EL_BLOCKED)
5384     {
5385       /* element is moving, but target field is not free (blocked), but
5386          already occupied by something different (example: acid pool);
5387          in this case, only remove the moving field, but not the target */
5388
5389       RemoveField(oldx, oldy);
5390
5391       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5392
5393       TEST_DrawLevelField(oldx, oldy);
5394
5395       return;
5396     }
5397   }
5398   else if (element == EL_BLOCKED)
5399   {
5400     Blocked2Moving(x, y, &oldx, &oldy);
5401     if (!IS_MOVING(oldx, oldy))
5402       return;
5403   }
5404
5405   if (element == EL_BLOCKED &&
5406       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5407        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5408        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5409        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5410        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5411        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5412     next_element = get_next_element(Feld[oldx][oldy]);
5413
5414   RemoveField(oldx, oldy);
5415   RemoveField(newx, newy);
5416
5417   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5418
5419   if (next_element != EL_UNDEFINED)
5420     Feld[oldx][oldy] = next_element;
5421
5422   TEST_DrawLevelField(oldx, oldy);
5423   TEST_DrawLevelField(newx, newy);
5424 }
5425
5426 void DrawDynamite(int x, int y)
5427 {
5428   int sx = SCREENX(x), sy = SCREENY(y);
5429   int graphic = el2img(Feld[x][y]);
5430   int frame;
5431
5432   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5433     return;
5434
5435   if (IS_WALKABLE_INSIDE(Back[x][y]))
5436     return;
5437
5438   if (Back[x][y])
5439     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5440   else if (Store[x][y])
5441     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5442
5443   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5444
5445   if (Back[x][y] || Store[x][y])
5446     DrawGraphicThruMask(sx, sy, graphic, frame);
5447   else
5448     DrawGraphic(sx, sy, graphic, frame);
5449 }
5450
5451 void CheckDynamite(int x, int y)
5452 {
5453   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5454   {
5455     MovDelay[x][y]--;
5456
5457     if (MovDelay[x][y] != 0)
5458     {
5459       DrawDynamite(x, y);
5460       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5461
5462       return;
5463     }
5464   }
5465
5466   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5467
5468   Bang(x, y);
5469 }
5470
5471 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5472 {
5473   boolean num_checked_players = 0;
5474   int i;
5475
5476   for (i = 0; i < MAX_PLAYERS; i++)
5477   {
5478     if (stored_player[i].active)
5479     {
5480       int sx = stored_player[i].jx;
5481       int sy = stored_player[i].jy;
5482
5483       if (num_checked_players == 0)
5484       {
5485         *sx1 = *sx2 = sx;
5486         *sy1 = *sy2 = sy;
5487       }
5488       else
5489       {
5490         *sx1 = MIN(*sx1, sx);
5491         *sy1 = MIN(*sy1, sy);
5492         *sx2 = MAX(*sx2, sx);
5493         *sy2 = MAX(*sy2, sy);
5494       }
5495
5496       num_checked_players++;
5497     }
5498   }
5499 }
5500
5501 static boolean checkIfAllPlayersFitToScreen_RND()
5502 {
5503   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5504
5505   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5506
5507   return (sx2 - sx1 < SCR_FIELDX &&
5508           sy2 - sy1 < SCR_FIELDY);
5509 }
5510
5511 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5512 {
5513   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5514
5515   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5516
5517   *sx = (sx1 + sx2) / 2;
5518   *sy = (sy1 + sy2) / 2;
5519 }
5520
5521 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5522                         boolean center_screen, boolean quick_relocation)
5523 {
5524   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5525   boolean no_delay = (tape.warp_forward);
5526   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5527   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5528
5529   if (quick_relocation)
5530   {
5531     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5532     {
5533       if (!level.shifted_relocation || center_screen)
5534       {
5535         /* quick relocation (without scrolling), with centering of screen */
5536
5537         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5538                     x > SBX_Right + MIDPOSX ? SBX_Right :
5539                     x - MIDPOSX);
5540
5541         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5542                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5543                     y - MIDPOSY);
5544       }
5545       else
5546       {
5547         /* quick relocation (without scrolling), but do not center screen */
5548
5549         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5550                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5551                                old_x - MIDPOSX);
5552
5553         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5554                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5555                                old_y - MIDPOSY);
5556
5557         int offset_x = x + (scroll_x - center_scroll_x);
5558         int offset_y = y + (scroll_y - center_scroll_y);
5559
5560         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5561                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5562                     offset_x - MIDPOSX);
5563
5564         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5565                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5566                     offset_y - MIDPOSY);
5567       }
5568     }
5569     else
5570     {
5571 #if 1
5572       if (!level.shifted_relocation || center_screen)
5573       {
5574         /* quick relocation (without scrolling), with centering of screen */
5575
5576         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5577                     x > SBX_Right + MIDPOSX ? SBX_Right :
5578                     x - MIDPOSX);
5579
5580         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5581                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5582                     y - MIDPOSY);
5583       }
5584       else
5585       {
5586         /* quick relocation (without scrolling), but do not center screen */
5587
5588         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5589                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5590                                old_x - MIDPOSX);
5591
5592         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5593                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5594                                old_y - MIDPOSY);
5595
5596         int offset_x = x + (scroll_x - center_scroll_x);
5597         int offset_y = y + (scroll_y - center_scroll_y);
5598
5599         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5600                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5601                     offset_x - MIDPOSX);
5602
5603         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5604                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5605                     offset_y - MIDPOSY);
5606       }
5607 #else
5608       /* quick relocation (without scrolling), inside visible screen area */
5609
5610       int offset = game.scroll_delay_value;
5611
5612       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5613           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5614         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5615
5616       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5617           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5618         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5619
5620       /* don't scroll over playfield boundaries */
5621       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5622         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5623
5624       /* don't scroll over playfield boundaries */
5625       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5626         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5627 #endif
5628     }
5629
5630     RedrawPlayfield(TRUE, 0,0,0,0);
5631   }
5632   else
5633   {
5634 #if 1
5635     int scroll_xx, scroll_yy;
5636
5637     if (!level.shifted_relocation || center_screen)
5638     {
5639       /* visible relocation (with scrolling), with centering of screen */
5640
5641       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5642                    x > SBX_Right + MIDPOSX ? SBX_Right :
5643                    x - MIDPOSX);
5644
5645       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5646                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5647                    y - MIDPOSY);
5648     }
5649     else
5650     {
5651       /* visible relocation (with scrolling), but do not center screen */
5652
5653       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5654                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5655                              old_x - MIDPOSX);
5656
5657       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5658                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5659                              old_y - MIDPOSY);
5660
5661       int offset_x = x + (scroll_x - center_scroll_x);
5662       int offset_y = y + (scroll_y - center_scroll_y);
5663
5664       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5665                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5666                    offset_x - MIDPOSX);
5667
5668       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5669                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5670                    offset_y - MIDPOSY);
5671     }
5672
5673 #else
5674
5675     /* visible relocation (with scrolling), with centering of screen */
5676
5677     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5678                      x > SBX_Right + MIDPOSX ? SBX_Right :
5679                      x - MIDPOSX);
5680
5681     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5682                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5683                      y - MIDPOSY);
5684 #endif
5685
5686     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5687
5688     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5689     {
5690       int dx = 0, dy = 0;
5691       int fx = FX, fy = FY;
5692
5693       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5694       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5695
5696       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5697         break;
5698
5699       scroll_x -= dx;
5700       scroll_y -= dy;
5701
5702       fx += dx * TILEX / 2;
5703       fy += dy * TILEY / 2;
5704
5705       ScrollLevel(dx, dy);
5706       DrawAllPlayers();
5707
5708       /* scroll in two steps of half tile size to make things smoother */
5709       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5710       FlushDisplay();
5711       Delay(wait_delay_value);
5712
5713       /* scroll second step to align at full tile size */
5714       BackToFront();
5715       Delay(wait_delay_value);
5716     }
5717
5718     DrawAllPlayers();
5719     BackToFront();
5720     Delay(wait_delay_value);
5721   }
5722 }
5723
5724 void RelocatePlayer(int jx, int jy, int el_player_raw)
5725 {
5726   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5727   int player_nr = GET_PLAYER_NR(el_player);
5728   struct PlayerInfo *player = &stored_player[player_nr];
5729   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5730   boolean no_delay = (tape.warp_forward);
5731   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5732   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5733   int old_jx = player->jx;
5734   int old_jy = player->jy;
5735   int old_element = Feld[old_jx][old_jy];
5736   int element = Feld[jx][jy];
5737   boolean player_relocated = (old_jx != jx || old_jy != jy);
5738
5739   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5740   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5741   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5742   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5743   int leave_side_horiz = move_dir_horiz;
5744   int leave_side_vert  = move_dir_vert;
5745   int enter_side = enter_side_horiz | enter_side_vert;
5746   int leave_side = leave_side_horiz | leave_side_vert;
5747
5748   if (player->GameOver)         /* do not reanimate dead player */
5749     return;
5750
5751   if (!player_relocated)        /* no need to relocate the player */
5752     return;
5753
5754   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5755   {
5756     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5757     DrawLevelField(jx, jy);
5758   }
5759
5760   if (player->present)
5761   {
5762     while (player->MovPos)
5763     {
5764       ScrollPlayer(player, SCROLL_GO_ON);
5765       ScrollScreen(NULL, SCROLL_GO_ON);
5766
5767       AdvanceFrameAndPlayerCounters(player->index_nr);
5768
5769       DrawPlayer(player);
5770
5771       BackToFront();
5772       Delay(wait_delay_value);
5773     }
5774
5775     DrawPlayer(player);         /* needed here only to cleanup last field */
5776     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5777
5778     player->is_moving = FALSE;
5779   }
5780
5781   if (IS_CUSTOM_ELEMENT(old_element))
5782     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5783                                CE_LEFT_BY_PLAYER,
5784                                player->index_bit, leave_side);
5785
5786   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5787                                       CE_PLAYER_LEAVES_X,
5788                                       player->index_bit, leave_side);
5789
5790   Feld[jx][jy] = el_player;
5791   InitPlayerField(jx, jy, el_player, TRUE);
5792
5793   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5794      possible that the relocation target field did not contain a player element,
5795      but a walkable element, to which the new player was relocated -- in this
5796      case, restore that (already initialized!) element on the player field */
5797   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5798   {
5799     Feld[jx][jy] = element;     /* restore previously existing element */
5800 #if 0
5801     /* !!! do not initialize already initialized element a second time !!! */
5802     /* (this causes at least problems with "element creation" CE trigger for
5803        already existing elements, and existing Sokoban fields counted twice) */
5804     InitField(jx, jy, FALSE);
5805 #endif
5806   }
5807
5808   /* only visually relocate centered player */
5809   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5810                      FALSE, level.instant_relocation);
5811
5812   TestIfPlayerTouchesBadThing(jx, jy);
5813   TestIfPlayerTouchesCustomElement(jx, jy);
5814
5815   if (IS_CUSTOM_ELEMENT(element))
5816     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5817                                player->index_bit, enter_side);
5818
5819   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5820                                       player->index_bit, enter_side);
5821
5822 #if 1
5823   if (player->is_switching)
5824   {
5825     /* ensure that relocation while still switching an element does not cause
5826        a new element to be treated as also switched directly after relocation
5827        (this is important for teleporter switches that teleport the player to
5828        a place where another teleporter switch is in the same direction, which
5829        would then incorrectly be treated as immediately switched before the
5830        direction key that caused the switch was released) */
5831
5832     player->switch_x += jx - old_jx;
5833     player->switch_y += jy - old_jy;
5834   }
5835 #endif
5836 }
5837
5838 void Explode(int ex, int ey, int phase, int mode)
5839 {
5840   int x, y;
5841   int last_phase;
5842   int border_element;
5843
5844   /* !!! eliminate this variable !!! */
5845   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5846
5847   if (game.explosions_delayed)
5848   {
5849     ExplodeField[ex][ey] = mode;
5850     return;
5851   }
5852
5853   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5854   {
5855     int center_element = Feld[ex][ey];
5856     int artwork_element, explosion_element;     /* set these values later */
5857
5858 #if 0
5859     /* --- This is only really needed (and now handled) in "Impact()". --- */
5860     /* do not explode moving elements that left the explode field in time */
5861     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5862         center_element == EL_EMPTY &&
5863         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5864       return;
5865 #endif
5866
5867 #if 0
5868     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5869     if (mode == EX_TYPE_NORMAL ||
5870         mode == EX_TYPE_CENTER ||
5871         mode == EX_TYPE_CROSS)
5872       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5873 #endif
5874
5875     /* remove things displayed in background while burning dynamite */
5876     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5877       Back[ex][ey] = 0;
5878
5879     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5880     {
5881       /* put moving element to center field (and let it explode there) */
5882       center_element = MovingOrBlocked2Element(ex, ey);
5883       RemoveMovingField(ex, ey);
5884       Feld[ex][ey] = center_element;
5885     }
5886
5887     /* now "center_element" is finally determined -- set related values now */
5888     artwork_element = center_element;           /* for custom player artwork */
5889     explosion_element = center_element;         /* for custom player artwork */
5890
5891     if (IS_PLAYER(ex, ey))
5892     {
5893       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5894
5895       artwork_element = stored_player[player_nr].artwork_element;
5896
5897       if (level.use_explosion_element[player_nr])
5898       {
5899         explosion_element = level.explosion_element[player_nr];
5900         artwork_element = explosion_element;
5901       }
5902     }
5903
5904 #if 1
5905     if (mode == EX_TYPE_NORMAL ||
5906         mode == EX_TYPE_CENTER ||
5907         mode == EX_TYPE_CROSS)
5908       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5909 #endif
5910
5911     last_phase = element_info[explosion_element].explosion_delay + 1;
5912
5913     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5914     {
5915       int xx = x - ex + 1;
5916       int yy = y - ey + 1;
5917       int element;
5918
5919       if (!IN_LEV_FIELD(x, y) ||
5920           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5921           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5922         continue;
5923
5924       element = Feld[x][y];
5925
5926       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5927       {
5928         element = MovingOrBlocked2Element(x, y);
5929
5930         if (!IS_EXPLOSION_PROOF(element))
5931           RemoveMovingField(x, y);
5932       }
5933
5934       /* indestructible elements can only explode in center (but not flames) */
5935       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5936                                            mode == EX_TYPE_BORDER)) ||
5937           element == EL_FLAMES)
5938         continue;
5939
5940       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5941          behaviour, for example when touching a yamyam that explodes to rocks
5942          with active deadly shield, a rock is created under the player !!! */
5943       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5944 #if 0
5945       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5946           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5947            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5948 #else
5949       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5950 #endif
5951       {
5952         if (IS_ACTIVE_BOMB(element))
5953         {
5954           /* re-activate things under the bomb like gate or penguin */
5955           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5956           Back[x][y] = 0;
5957         }
5958
5959         continue;
5960       }
5961
5962       /* save walkable background elements while explosion on same tile */
5963       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5964           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5965         Back[x][y] = element;
5966
5967       /* ignite explodable elements reached by other explosion */
5968       if (element == EL_EXPLOSION)
5969         element = Store2[x][y];
5970
5971       if (AmoebaNr[x][y] &&
5972           (element == EL_AMOEBA_FULL ||
5973            element == EL_BD_AMOEBA ||
5974            element == EL_AMOEBA_GROWING))
5975       {
5976         AmoebaCnt[AmoebaNr[x][y]]--;
5977         AmoebaCnt2[AmoebaNr[x][y]]--;
5978       }
5979
5980       RemoveField(x, y);
5981
5982       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5983       {
5984         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5985
5986         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5987
5988         if (PLAYERINFO(ex, ey)->use_murphy)
5989           Store[x][y] = EL_EMPTY;
5990       }
5991
5992       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5993          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5994       else if (ELEM_IS_PLAYER(center_element))
5995         Store[x][y] = EL_EMPTY;
5996       else if (center_element == EL_YAMYAM)
5997         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5998       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5999         Store[x][y] = element_info[center_element].content.e[xx][yy];
6000 #if 1
6001       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6002          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6003          otherwise) -- FIX THIS !!! */
6004       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6005         Store[x][y] = element_info[element].content.e[1][1];
6006 #else
6007       else if (!CAN_EXPLODE(element))
6008         Store[x][y] = element_info[element].content.e[1][1];
6009 #endif
6010       else
6011         Store[x][y] = EL_EMPTY;
6012
6013       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6014           center_element == EL_AMOEBA_TO_DIAMOND)
6015         Store2[x][y] = element;
6016
6017       Feld[x][y] = EL_EXPLOSION;
6018       GfxElement[x][y] = artwork_element;
6019
6020       ExplodePhase[x][y] = 1;
6021       ExplodeDelay[x][y] = last_phase;
6022
6023       Stop[x][y] = TRUE;
6024     }
6025
6026     if (center_element == EL_YAMYAM)
6027       game.yamyam_content_nr =
6028         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6029
6030     return;
6031   }
6032
6033   if (Stop[ex][ey])
6034     return;
6035
6036   x = ex;
6037   y = ey;
6038
6039   if (phase == 1)
6040     GfxFrame[x][y] = 0;         /* restart explosion animation */
6041
6042   last_phase = ExplodeDelay[x][y];
6043
6044   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6045
6046 #ifdef DEBUG
6047
6048   /* activate this even in non-DEBUG version until cause for crash in
6049      getGraphicAnimationFrame() (see below) is found and eliminated */
6050
6051 #endif
6052 #if 1
6053
6054 #if 1
6055   /* this can happen if the player leaves an explosion just in time */
6056   if (GfxElement[x][y] == EL_UNDEFINED)
6057     GfxElement[x][y] = EL_EMPTY;
6058 #else
6059   if (GfxElement[x][y] == EL_UNDEFINED)
6060   {
6061     printf("\n\n");
6062     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6063     printf("Explode(): This should never happen!\n");
6064     printf("\n\n");
6065
6066     GfxElement[x][y] = EL_EMPTY;
6067   }
6068 #endif
6069
6070 #endif
6071
6072   border_element = Store2[x][y];
6073   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6074     border_element = StorePlayer[x][y];
6075
6076   if (phase == element_info[border_element].ignition_delay ||
6077       phase == last_phase)
6078   {
6079     boolean border_explosion = FALSE;
6080
6081     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6082         !PLAYER_EXPLOSION_PROTECTED(x, y))
6083     {
6084       KillPlayerUnlessExplosionProtected(x, y);
6085       border_explosion = TRUE;
6086     }
6087     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6088     {
6089       Feld[x][y] = Store2[x][y];
6090       Store2[x][y] = 0;
6091       Bang(x, y);
6092       border_explosion = TRUE;
6093     }
6094     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6095     {
6096       AmoebeUmwandeln(x, y);
6097       Store2[x][y] = 0;
6098       border_explosion = TRUE;
6099     }
6100
6101     /* if an element just explodes due to another explosion (chain-reaction),
6102        do not immediately end the new explosion when it was the last frame of
6103        the explosion (as it would be done in the following "if"-statement!) */
6104     if (border_explosion && phase == last_phase)
6105       return;
6106   }
6107
6108   if (phase == last_phase)
6109   {
6110     int element;
6111
6112     element = Feld[x][y] = Store[x][y];
6113     Store[x][y] = Store2[x][y] = 0;
6114     GfxElement[x][y] = EL_UNDEFINED;
6115
6116     /* player can escape from explosions and might therefore be still alive */
6117     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6118         element <= EL_PLAYER_IS_EXPLODING_4)
6119     {
6120       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6121       int explosion_element = EL_PLAYER_1 + player_nr;
6122       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6123       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6124
6125       if (level.use_explosion_element[player_nr])
6126         explosion_element = level.explosion_element[player_nr];
6127
6128       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6129                     element_info[explosion_element].content.e[xx][yy]);
6130     }
6131
6132     /* restore probably existing indestructible background element */
6133     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6134       element = Feld[x][y] = Back[x][y];
6135     Back[x][y] = 0;
6136
6137     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6138     GfxDir[x][y] = MV_NONE;
6139     ChangeDelay[x][y] = 0;
6140     ChangePage[x][y] = -1;
6141
6142 #if USE_NEW_CUSTOM_VALUE
6143     CustomValue[x][y] = 0;
6144 #endif
6145
6146     InitField_WithBug2(x, y, FALSE);
6147
6148     TEST_DrawLevelField(x, y);
6149
6150     TestIfElementTouchesCustomElement(x, y);
6151
6152     if (GFX_CRUMBLED(element))
6153       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6154
6155     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6156       StorePlayer[x][y] = 0;
6157
6158     if (ELEM_IS_PLAYER(element))
6159       RelocatePlayer(x, y, element);
6160   }
6161   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6162   {
6163     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6164     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6165
6166     if (phase == delay)
6167       TEST_DrawLevelFieldCrumbledSand(x, y);
6168
6169     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6170     {
6171       DrawLevelElement(x, y, Back[x][y]);
6172       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6173     }
6174     else if (IS_WALKABLE_UNDER(Back[x][y]))
6175     {
6176       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6177       DrawLevelElementThruMask(x, y, Back[x][y]);
6178     }
6179     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6180       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6181   }
6182 }
6183
6184 void DynaExplode(int ex, int ey)
6185 {
6186   int i, j;
6187   int dynabomb_element = Feld[ex][ey];
6188   int dynabomb_size = 1;
6189   boolean dynabomb_xl = FALSE;
6190   struct PlayerInfo *player;
6191   static int xy[4][2] =
6192   {
6193     { 0, -1 },
6194     { -1, 0 },
6195     { +1, 0 },
6196     { 0, +1 }
6197   };
6198
6199   if (IS_ACTIVE_BOMB(dynabomb_element))
6200   {
6201     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6202     dynabomb_size = player->dynabomb_size;
6203     dynabomb_xl = player->dynabomb_xl;
6204     player->dynabombs_left++;
6205   }
6206
6207   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6208
6209   for (i = 0; i < NUM_DIRECTIONS; i++)
6210   {
6211     for (j = 1; j <= dynabomb_size; j++)
6212     {
6213       int x = ex + j * xy[i][0];
6214       int y = ey + j * xy[i][1];
6215       int element;
6216
6217       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6218         break;
6219
6220       element = Feld[x][y];
6221
6222       /* do not restart explosions of fields with active bombs */
6223       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6224         continue;
6225
6226       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6227
6228       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6229           !IS_DIGGABLE(element) && !dynabomb_xl)
6230         break;
6231     }
6232   }
6233 }
6234
6235 void Bang(int x, int y)
6236 {
6237   int element = MovingOrBlocked2Element(x, y);
6238   int explosion_type = EX_TYPE_NORMAL;
6239
6240   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6241   {
6242     struct PlayerInfo *player = PLAYERINFO(x, y);
6243
6244 #if USE_FIX_CE_ACTION_WITH_PLAYER
6245     element = Feld[x][y] = player->initial_element;
6246 #else
6247     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6248                             player->element_nr);
6249 #endif
6250
6251     if (level.use_explosion_element[player->index_nr])
6252     {
6253       int explosion_element = level.explosion_element[player->index_nr];
6254
6255       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6256         explosion_type = EX_TYPE_CROSS;
6257       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6258         explosion_type = EX_TYPE_CENTER;
6259     }
6260   }
6261
6262   switch (element)
6263   {
6264     case EL_BUG:
6265     case EL_SPACESHIP:
6266     case EL_BD_BUTTERFLY:
6267     case EL_BD_FIREFLY:
6268     case EL_YAMYAM:
6269     case EL_DARK_YAMYAM:
6270     case EL_ROBOT:
6271     case EL_PACMAN:
6272     case EL_MOLE:
6273       RaiseScoreElement(element);
6274       break;
6275
6276     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6277     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6278     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6279     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6280     case EL_DYNABOMB_INCREASE_NUMBER:
6281     case EL_DYNABOMB_INCREASE_SIZE:
6282     case EL_DYNABOMB_INCREASE_POWER:
6283       explosion_type = EX_TYPE_DYNA;
6284       break;
6285
6286     case EL_DC_LANDMINE:
6287 #if 0
6288     case EL_EM_EXIT_OPEN:
6289     case EL_EM_STEEL_EXIT_OPEN:
6290 #endif
6291       explosion_type = EX_TYPE_CENTER;
6292       break;
6293
6294     case EL_PENGUIN:
6295     case EL_LAMP:
6296     case EL_LAMP_ACTIVE:
6297     case EL_AMOEBA_TO_DIAMOND:
6298       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6299         explosion_type = EX_TYPE_CENTER;
6300       break;
6301
6302     default:
6303       if (element_info[element].explosion_type == EXPLODES_CROSS)
6304         explosion_type = EX_TYPE_CROSS;
6305       else if (element_info[element].explosion_type == EXPLODES_1X1)
6306         explosion_type = EX_TYPE_CENTER;
6307       break;
6308   }
6309
6310   if (explosion_type == EX_TYPE_DYNA)
6311     DynaExplode(x, y);
6312   else
6313     Explode(x, y, EX_PHASE_START, explosion_type);
6314
6315   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6316 }
6317
6318 void SplashAcid(int x, int y)
6319 {
6320   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6321       (!IN_LEV_FIELD(x - 1, y - 2) ||
6322        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6323     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6324
6325   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6326       (!IN_LEV_FIELD(x + 1, y - 2) ||
6327        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6328     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6329
6330   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6331 }
6332
6333 static void InitBeltMovement()
6334 {
6335   static int belt_base_element[4] =
6336   {
6337     EL_CONVEYOR_BELT_1_LEFT,
6338     EL_CONVEYOR_BELT_2_LEFT,
6339     EL_CONVEYOR_BELT_3_LEFT,
6340     EL_CONVEYOR_BELT_4_LEFT
6341   };
6342   static int belt_base_active_element[4] =
6343   {
6344     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6345     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6346     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6347     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6348   };
6349
6350   int x, y, i, j;
6351
6352   /* set frame order for belt animation graphic according to belt direction */
6353   for (i = 0; i < NUM_BELTS; i++)
6354   {
6355     int belt_nr = i;
6356
6357     for (j = 0; j < NUM_BELT_PARTS; j++)
6358     {
6359       int element = belt_base_active_element[belt_nr] + j;
6360       int graphic_1 = el2img(element);
6361       int graphic_2 = el2panelimg(element);
6362
6363       if (game.belt_dir[i] == MV_LEFT)
6364       {
6365         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6366         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6367       }
6368       else
6369       {
6370         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6371         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6372       }
6373     }
6374   }
6375
6376   SCAN_PLAYFIELD(x, y)
6377   {
6378     int element = Feld[x][y];
6379
6380     for (i = 0; i < NUM_BELTS; i++)
6381     {
6382       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6383       {
6384         int e_belt_nr = getBeltNrFromBeltElement(element);
6385         int belt_nr = i;
6386
6387         if (e_belt_nr == belt_nr)
6388         {
6389           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6390
6391           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6392         }
6393       }
6394     }
6395   }
6396 }
6397
6398 static void ToggleBeltSwitch(int x, int y)
6399 {
6400   static int belt_base_element[4] =
6401   {
6402     EL_CONVEYOR_BELT_1_LEFT,
6403     EL_CONVEYOR_BELT_2_LEFT,
6404     EL_CONVEYOR_BELT_3_LEFT,
6405     EL_CONVEYOR_BELT_4_LEFT
6406   };
6407   static int belt_base_active_element[4] =
6408   {
6409     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6410     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6411     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6412     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6413   };
6414   static int belt_base_switch_element[4] =
6415   {
6416     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6417     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6418     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6419     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6420   };
6421   static int belt_move_dir[4] =
6422   {
6423     MV_LEFT,
6424     MV_NONE,
6425     MV_RIGHT,
6426     MV_NONE,
6427   };
6428
6429   int element = Feld[x][y];
6430   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6431   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6432   int belt_dir = belt_move_dir[belt_dir_nr];
6433   int xx, yy, i;
6434
6435   if (!IS_BELT_SWITCH(element))
6436     return;
6437
6438   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6439   game.belt_dir[belt_nr] = belt_dir;
6440
6441   if (belt_dir_nr == 3)
6442     belt_dir_nr = 1;
6443
6444   /* set frame order for belt animation graphic according to belt direction */
6445   for (i = 0; i < NUM_BELT_PARTS; i++)
6446   {
6447     int element = belt_base_active_element[belt_nr] + i;
6448     int graphic_1 = el2img(element);
6449     int graphic_2 = el2panelimg(element);
6450
6451     if (belt_dir == MV_LEFT)
6452     {
6453       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6454       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6455     }
6456     else
6457     {
6458       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6459       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6460     }
6461   }
6462
6463   SCAN_PLAYFIELD(xx, yy)
6464   {
6465     int element = Feld[xx][yy];
6466
6467     if (IS_BELT_SWITCH(element))
6468     {
6469       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6470
6471       if (e_belt_nr == belt_nr)
6472       {
6473         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6474         TEST_DrawLevelField(xx, yy);
6475       }
6476     }
6477     else if (IS_BELT(element) && belt_dir != MV_NONE)
6478     {
6479       int e_belt_nr = getBeltNrFromBeltElement(element);
6480
6481       if (e_belt_nr == belt_nr)
6482       {
6483         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6484
6485         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6486         TEST_DrawLevelField(xx, yy);
6487       }
6488     }
6489     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6490     {
6491       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6492
6493       if (e_belt_nr == belt_nr)
6494       {
6495         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6496
6497         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6498         TEST_DrawLevelField(xx, yy);
6499       }
6500     }
6501   }
6502 }
6503
6504 static void ToggleSwitchgateSwitch(int x, int y)
6505 {
6506   int xx, yy;
6507
6508   game.switchgate_pos = !game.switchgate_pos;
6509
6510   SCAN_PLAYFIELD(xx, yy)
6511   {
6512     int element = Feld[xx][yy];
6513
6514 #if !USE_BOTH_SWITCHGATE_SWITCHES
6515     if (element == EL_SWITCHGATE_SWITCH_UP ||
6516         element == EL_SWITCHGATE_SWITCH_DOWN)
6517     {
6518       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6519       TEST_DrawLevelField(xx, yy);
6520     }
6521     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6522              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6523     {
6524       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6525       TEST_DrawLevelField(xx, yy);
6526     }
6527 #else
6528     if (element == EL_SWITCHGATE_SWITCH_UP)
6529     {
6530       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6531       TEST_DrawLevelField(xx, yy);
6532     }
6533     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6534     {
6535       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6536       TEST_DrawLevelField(xx, yy);
6537     }
6538     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6539     {
6540       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6541       TEST_DrawLevelField(xx, yy);
6542     }
6543     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6544     {
6545       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6546       TEST_DrawLevelField(xx, yy);
6547     }
6548 #endif
6549     else if (element == EL_SWITCHGATE_OPEN ||
6550              element == EL_SWITCHGATE_OPENING)
6551     {
6552       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6553
6554       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6555     }
6556     else if (element == EL_SWITCHGATE_CLOSED ||
6557              element == EL_SWITCHGATE_CLOSING)
6558     {
6559       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6560
6561       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6562     }
6563   }
6564 }
6565
6566 static int getInvisibleActiveFromInvisibleElement(int element)
6567 {
6568   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6569           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6570           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6571           element);
6572 }
6573
6574 static int getInvisibleFromInvisibleActiveElement(int element)
6575 {
6576   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6577           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6578           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6579           element);
6580 }
6581
6582 static void RedrawAllLightSwitchesAndInvisibleElements()
6583 {
6584   int x, y;
6585
6586   SCAN_PLAYFIELD(x, y)
6587   {
6588     int element = Feld[x][y];
6589
6590     if (element == EL_LIGHT_SWITCH &&
6591         game.light_time_left > 0)
6592     {
6593       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6594       TEST_DrawLevelField(x, y);
6595     }
6596     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6597              game.light_time_left == 0)
6598     {
6599       Feld[x][y] = EL_LIGHT_SWITCH;
6600       TEST_DrawLevelField(x, y);
6601     }
6602     else if (element == EL_EMC_DRIPPER &&
6603              game.light_time_left > 0)
6604     {
6605       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6606       TEST_DrawLevelField(x, y);
6607     }
6608     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6609              game.light_time_left == 0)
6610     {
6611       Feld[x][y] = EL_EMC_DRIPPER;
6612       TEST_DrawLevelField(x, y);
6613     }
6614     else if (element == EL_INVISIBLE_STEELWALL ||
6615              element == EL_INVISIBLE_WALL ||
6616              element == EL_INVISIBLE_SAND)
6617     {
6618       if (game.light_time_left > 0)
6619         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6620
6621       TEST_DrawLevelField(x, y);
6622
6623       /* uncrumble neighbour fields, if needed */
6624       if (element == EL_INVISIBLE_SAND)
6625         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6626     }
6627     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6628              element == EL_INVISIBLE_WALL_ACTIVE ||
6629              element == EL_INVISIBLE_SAND_ACTIVE)
6630     {
6631       if (game.light_time_left == 0)
6632         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6633
6634       TEST_DrawLevelField(x, y);
6635
6636       /* re-crumble neighbour fields, if needed */
6637       if (element == EL_INVISIBLE_SAND)
6638         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6639     }
6640   }
6641 }
6642
6643 static void RedrawAllInvisibleElementsForLenses()
6644 {
6645   int x, y;
6646
6647   SCAN_PLAYFIELD(x, y)
6648   {
6649     int element = Feld[x][y];
6650
6651     if (element == EL_EMC_DRIPPER &&
6652         game.lenses_time_left > 0)
6653     {
6654       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6655       TEST_DrawLevelField(x, y);
6656     }
6657     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6658              game.lenses_time_left == 0)
6659     {
6660       Feld[x][y] = EL_EMC_DRIPPER;
6661       TEST_DrawLevelField(x, y);
6662     }
6663     else if (element == EL_INVISIBLE_STEELWALL ||
6664              element == EL_INVISIBLE_WALL ||
6665              element == EL_INVISIBLE_SAND)
6666     {
6667       if (game.lenses_time_left > 0)
6668         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6669
6670       TEST_DrawLevelField(x, y);
6671
6672       /* uncrumble neighbour fields, if needed */
6673       if (element == EL_INVISIBLE_SAND)
6674         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6675     }
6676     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6677              element == EL_INVISIBLE_WALL_ACTIVE ||
6678              element == EL_INVISIBLE_SAND_ACTIVE)
6679     {
6680       if (game.lenses_time_left == 0)
6681         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6682
6683       TEST_DrawLevelField(x, y);
6684
6685       /* re-crumble neighbour fields, if needed */
6686       if (element == EL_INVISIBLE_SAND)
6687         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6688     }
6689   }
6690 }
6691
6692 static void RedrawAllInvisibleElementsForMagnifier()
6693 {
6694   int x, y;
6695
6696   SCAN_PLAYFIELD(x, y)
6697   {
6698     int element = Feld[x][y];
6699
6700     if (element == EL_EMC_FAKE_GRASS &&
6701         game.magnify_time_left > 0)
6702     {
6703       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6704       TEST_DrawLevelField(x, y);
6705     }
6706     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6707              game.magnify_time_left == 0)
6708     {
6709       Feld[x][y] = EL_EMC_FAKE_GRASS;
6710       TEST_DrawLevelField(x, y);
6711     }
6712     else if (IS_GATE_GRAY(element) &&
6713              game.magnify_time_left > 0)
6714     {
6715       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6716                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6717                     IS_EM_GATE_GRAY(element) ?
6718                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6719                     IS_EMC_GATE_GRAY(element) ?
6720                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6721                     IS_DC_GATE_GRAY(element) ?
6722                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6723                     element);
6724       TEST_DrawLevelField(x, y);
6725     }
6726     else if (IS_GATE_GRAY_ACTIVE(element) &&
6727              game.magnify_time_left == 0)
6728     {
6729       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6730                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6731                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6732                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6733                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6734                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6735                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6736                     EL_DC_GATE_WHITE_GRAY :
6737                     element);
6738       TEST_DrawLevelField(x, y);
6739     }
6740   }
6741 }
6742
6743 static void ToggleLightSwitch(int x, int y)
6744 {
6745   int element = Feld[x][y];
6746
6747   game.light_time_left =
6748     (element == EL_LIGHT_SWITCH ?
6749      level.time_light * FRAMES_PER_SECOND : 0);
6750
6751   RedrawAllLightSwitchesAndInvisibleElements();
6752 }
6753
6754 static void ActivateTimegateSwitch(int x, int y)
6755 {
6756   int xx, yy;
6757
6758   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6759
6760   SCAN_PLAYFIELD(xx, yy)
6761   {
6762     int element = Feld[xx][yy];
6763
6764     if (element == EL_TIMEGATE_CLOSED ||
6765         element == EL_TIMEGATE_CLOSING)
6766     {
6767       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6768       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6769     }
6770
6771     /*
6772     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6773     {
6774       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6775       TEST_DrawLevelField(xx, yy);
6776     }
6777     */
6778
6779   }
6780
6781 #if 1
6782   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6783                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6784 #else
6785   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6786 #endif
6787 }
6788
6789 void Impact(int x, int y)
6790 {
6791   boolean last_line = (y == lev_fieldy - 1);
6792   boolean object_hit = FALSE;
6793   boolean impact = (last_line || object_hit);
6794   int element = Feld[x][y];
6795   int smashed = EL_STEELWALL;
6796
6797   if (!last_line)       /* check if element below was hit */
6798   {
6799     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6800       return;
6801
6802     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6803                                          MovDir[x][y + 1] != MV_DOWN ||
6804                                          MovPos[x][y + 1] <= TILEY / 2));
6805
6806     /* do not smash moving elements that left the smashed field in time */
6807     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6808         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6809       object_hit = FALSE;
6810
6811 #if USE_QUICKSAND_IMPACT_BUGFIX
6812     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6813     {
6814       RemoveMovingField(x, y + 1);
6815       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6816       Feld[x][y + 2] = EL_ROCK;
6817       TEST_DrawLevelField(x, y + 2);
6818
6819       object_hit = TRUE;
6820     }
6821
6822     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6823     {
6824       RemoveMovingField(x, y + 1);
6825       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6826       Feld[x][y + 2] = EL_ROCK;
6827       TEST_DrawLevelField(x, y + 2);
6828
6829       object_hit = TRUE;
6830     }
6831 #endif
6832
6833     if (object_hit)
6834       smashed = MovingOrBlocked2Element(x, y + 1);
6835
6836     impact = (last_line || object_hit);
6837   }
6838
6839   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6840   {
6841     SplashAcid(x, y + 1);
6842     return;
6843   }
6844
6845   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6846   /* only reset graphic animation if graphic really changes after impact */
6847   if (impact &&
6848       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6849   {
6850     ResetGfxAnimation(x, y);
6851     TEST_DrawLevelField(x, y);
6852   }
6853
6854   if (impact && CAN_EXPLODE_IMPACT(element))
6855   {
6856     Bang(x, y);
6857     return;
6858   }
6859   else if (impact && element == EL_PEARL &&
6860            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6861   {
6862     ResetGfxAnimation(x, y);
6863
6864     Feld[x][y] = EL_PEARL_BREAKING;
6865     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6866     return;
6867   }
6868   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6869   {
6870     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6871
6872     return;
6873   }
6874
6875   if (impact && element == EL_AMOEBA_DROP)
6876   {
6877     if (object_hit && IS_PLAYER(x, y + 1))
6878       KillPlayerUnlessEnemyProtected(x, y + 1);
6879     else if (object_hit && smashed == EL_PENGUIN)
6880       Bang(x, y + 1);
6881     else
6882     {
6883       Feld[x][y] = EL_AMOEBA_GROWING;
6884       Store[x][y] = EL_AMOEBA_WET;
6885
6886       ResetRandomAnimationValue(x, y);
6887     }
6888     return;
6889   }
6890
6891   if (object_hit)               /* check which object was hit */
6892   {
6893     if ((CAN_PASS_MAGIC_WALL(element) && 
6894          (smashed == EL_MAGIC_WALL ||
6895           smashed == EL_BD_MAGIC_WALL)) ||
6896         (CAN_PASS_DC_MAGIC_WALL(element) &&
6897          smashed == EL_DC_MAGIC_WALL))
6898     {
6899       int xx, yy;
6900       int activated_magic_wall =
6901         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6902          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6903          EL_DC_MAGIC_WALL_ACTIVE);
6904
6905       /* activate magic wall / mill */
6906       SCAN_PLAYFIELD(xx, yy)
6907       {
6908         if (Feld[xx][yy] == smashed)
6909           Feld[xx][yy] = activated_magic_wall;
6910       }
6911
6912       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6913       game.magic_wall_active = TRUE;
6914
6915       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6916                             SND_MAGIC_WALL_ACTIVATING :
6917                             smashed == EL_BD_MAGIC_WALL ?
6918                             SND_BD_MAGIC_WALL_ACTIVATING :
6919                             SND_DC_MAGIC_WALL_ACTIVATING));
6920     }
6921
6922     if (IS_PLAYER(x, y + 1))
6923     {
6924       if (CAN_SMASH_PLAYER(element))
6925       {
6926         KillPlayerUnlessEnemyProtected(x, y + 1);
6927         return;
6928       }
6929     }
6930     else if (smashed == EL_PENGUIN)
6931     {
6932       if (CAN_SMASH_PLAYER(element))
6933       {
6934         Bang(x, y + 1);
6935         return;
6936       }
6937     }
6938     else if (element == EL_BD_DIAMOND)
6939     {
6940       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6941       {
6942         Bang(x, y + 1);
6943         return;
6944       }
6945     }
6946     else if (((element == EL_SP_INFOTRON ||
6947                element == EL_SP_ZONK) &&
6948               (smashed == EL_SP_SNIKSNAK ||
6949                smashed == EL_SP_ELECTRON ||
6950                smashed == EL_SP_DISK_ORANGE)) ||
6951              (element == EL_SP_INFOTRON &&
6952               smashed == EL_SP_DISK_YELLOW))
6953     {
6954       Bang(x, y + 1);
6955       return;
6956     }
6957     else if (CAN_SMASH_EVERYTHING(element))
6958     {
6959       if (IS_CLASSIC_ENEMY(smashed) ||
6960           CAN_EXPLODE_SMASHED(smashed))
6961       {
6962         Bang(x, y + 1);
6963         return;
6964       }
6965       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6966       {
6967         if (smashed == EL_LAMP ||
6968             smashed == EL_LAMP_ACTIVE)
6969         {
6970           Bang(x, y + 1);
6971           return;
6972         }
6973         else if (smashed == EL_NUT)
6974         {
6975           Feld[x][y + 1] = EL_NUT_BREAKING;
6976           PlayLevelSound(x, y, SND_NUT_BREAKING);
6977           RaiseScoreElement(EL_NUT);
6978           return;
6979         }
6980         else if (smashed == EL_PEARL)
6981         {
6982           ResetGfxAnimation(x, y);
6983
6984           Feld[x][y + 1] = EL_PEARL_BREAKING;
6985           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6986           return;
6987         }
6988         else if (smashed == EL_DIAMOND)
6989         {
6990           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6991           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6992           return;
6993         }
6994         else if (IS_BELT_SWITCH(smashed))
6995         {
6996           ToggleBeltSwitch(x, y + 1);
6997         }
6998         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6999                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7000                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7001                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7002         {
7003           ToggleSwitchgateSwitch(x, y + 1);
7004         }
7005         else if (smashed == EL_LIGHT_SWITCH ||
7006                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7007         {
7008           ToggleLightSwitch(x, y + 1);
7009         }
7010         else
7011         {
7012 #if 0
7013           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7014 #endif
7015
7016           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7017
7018           CheckElementChangeBySide(x, y + 1, smashed, element,
7019                                    CE_SWITCHED, CH_SIDE_TOP);
7020           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7021                                             CH_SIDE_TOP);
7022         }
7023       }
7024       else
7025       {
7026         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7027       }
7028     }
7029   }
7030
7031   /* play sound of magic wall / mill */
7032   if (!last_line &&
7033       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7034        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7035        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7036   {
7037     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7038       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7039     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7040       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7041     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7042       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7043
7044     return;
7045   }
7046
7047   /* play sound of object that hits the ground */
7048   if (last_line || object_hit)
7049     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7050 }
7051
7052 inline static void TurnRoundExt(int x, int y)
7053 {
7054   static struct
7055   {
7056     int dx, dy;
7057   } move_xy[] =
7058   {
7059     {  0,  0 },
7060     { -1,  0 },
7061     { +1,  0 },
7062     {  0,  0 },
7063     {  0, -1 },
7064     {  0,  0 }, { 0, 0 }, { 0, 0 },
7065     {  0, +1 }
7066   };
7067   static struct
7068   {
7069     int left, right, back;
7070   } turn[] =
7071   {
7072     { 0,        0,              0        },
7073     { MV_DOWN,  MV_UP,          MV_RIGHT },
7074     { MV_UP,    MV_DOWN,        MV_LEFT  },
7075     { 0,        0,              0        },
7076     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7077     { 0,        0,              0        },
7078     { 0,        0,              0        },
7079     { 0,        0,              0        },
7080     { MV_RIGHT, MV_LEFT,        MV_UP    }
7081   };
7082
7083   int element = Feld[x][y];
7084   int move_pattern = element_info[element].move_pattern;
7085
7086   int old_move_dir = MovDir[x][y];
7087   int left_dir  = turn[old_move_dir].left;
7088   int right_dir = turn[old_move_dir].right;
7089   int back_dir  = turn[old_move_dir].back;
7090
7091   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7092   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7093   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7094   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7095
7096   int left_x  = x + left_dx,  left_y  = y + left_dy;
7097   int right_x = x + right_dx, right_y = y + right_dy;
7098   int move_x  = x + move_dx,  move_y  = y + move_dy;
7099
7100   int xx, yy;
7101
7102   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7103   {
7104     TestIfBadThingTouchesOtherBadThing(x, y);
7105
7106     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7107       MovDir[x][y] = right_dir;
7108     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7109       MovDir[x][y] = left_dir;
7110
7111     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7112       MovDelay[x][y] = 9;
7113     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7114       MovDelay[x][y] = 1;
7115   }
7116   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7117   {
7118     TestIfBadThingTouchesOtherBadThing(x, y);
7119
7120     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7121       MovDir[x][y] = left_dir;
7122     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7123       MovDir[x][y] = right_dir;
7124
7125     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7126       MovDelay[x][y] = 9;
7127     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7128       MovDelay[x][y] = 1;
7129   }
7130   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7131   {
7132     TestIfBadThingTouchesOtherBadThing(x, y);
7133
7134     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7135       MovDir[x][y] = left_dir;
7136     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7137       MovDir[x][y] = right_dir;
7138
7139     if (MovDir[x][y] != old_move_dir)
7140       MovDelay[x][y] = 9;
7141   }
7142   else if (element == EL_YAMYAM)
7143   {
7144     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7145     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7146
7147     if (can_turn_left && can_turn_right)
7148       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149     else if (can_turn_left)
7150       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151     else if (can_turn_right)
7152       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7153     else
7154       MovDir[x][y] = back_dir;
7155
7156     MovDelay[x][y] = 16 + 16 * RND(3);
7157   }
7158   else if (element == EL_DARK_YAMYAM)
7159   {
7160     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7161                                                          left_x, left_y);
7162     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7163                                                          right_x, right_y);
7164
7165     if (can_turn_left && can_turn_right)
7166       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7167     else if (can_turn_left)
7168       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7169     else if (can_turn_right)
7170       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7171     else
7172       MovDir[x][y] = back_dir;
7173
7174     MovDelay[x][y] = 16 + 16 * RND(3);
7175   }
7176   else if (element == EL_PACMAN)
7177   {
7178     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7179     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7180
7181     if (can_turn_left && can_turn_right)
7182       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7183     else if (can_turn_left)
7184       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7185     else if (can_turn_right)
7186       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7187     else
7188       MovDir[x][y] = back_dir;
7189
7190     MovDelay[x][y] = 6 + RND(40);
7191   }
7192   else if (element == EL_PIG)
7193   {
7194     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7195     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7196     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7197     boolean should_turn_left, should_turn_right, should_move_on;
7198     int rnd_value = 24;
7199     int rnd = RND(rnd_value);
7200
7201     should_turn_left = (can_turn_left &&
7202                         (!can_move_on ||
7203                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7204                                                    y + back_dy + left_dy)));
7205     should_turn_right = (can_turn_right &&
7206                          (!can_move_on ||
7207                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7208                                                     y + back_dy + right_dy)));
7209     should_move_on = (can_move_on &&
7210                       (!can_turn_left ||
7211                        !can_turn_right ||
7212                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7213                                                  y + move_dy + left_dy) ||
7214                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7215                                                  y + move_dy + right_dy)));
7216
7217     if (should_turn_left || should_turn_right || should_move_on)
7218     {
7219       if (should_turn_left && should_turn_right && should_move_on)
7220         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7221                         rnd < 2 * rnd_value / 3 ? right_dir :
7222                         old_move_dir);
7223       else if (should_turn_left && should_turn_right)
7224         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7225       else if (should_turn_left && should_move_on)
7226         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7227       else if (should_turn_right && should_move_on)
7228         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7229       else if (should_turn_left)
7230         MovDir[x][y] = left_dir;
7231       else if (should_turn_right)
7232         MovDir[x][y] = right_dir;
7233       else if (should_move_on)
7234         MovDir[x][y] = old_move_dir;
7235     }
7236     else if (can_move_on && rnd > rnd_value / 8)
7237       MovDir[x][y] = old_move_dir;
7238     else if (can_turn_left && can_turn_right)
7239       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7240     else if (can_turn_left && rnd > rnd_value / 8)
7241       MovDir[x][y] = left_dir;
7242     else if (can_turn_right && rnd > rnd_value/8)
7243       MovDir[x][y] = right_dir;
7244     else
7245       MovDir[x][y] = back_dir;
7246
7247     xx = x + move_xy[MovDir[x][y]].dx;
7248     yy = y + move_xy[MovDir[x][y]].dy;
7249
7250     if (!IN_LEV_FIELD(xx, yy) ||
7251         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7252       MovDir[x][y] = old_move_dir;
7253
7254     MovDelay[x][y] = 0;
7255   }
7256   else if (element == EL_DRAGON)
7257   {
7258     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7259     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7260     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7261     int rnd_value = 24;
7262     int rnd = RND(rnd_value);
7263
7264     if (can_move_on && rnd > rnd_value / 8)
7265       MovDir[x][y] = old_move_dir;
7266     else if (can_turn_left && can_turn_right)
7267       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7268     else if (can_turn_left && rnd > rnd_value / 8)
7269       MovDir[x][y] = left_dir;
7270     else if (can_turn_right && rnd > rnd_value / 8)
7271       MovDir[x][y] = right_dir;
7272     else
7273       MovDir[x][y] = back_dir;
7274
7275     xx = x + move_xy[MovDir[x][y]].dx;
7276     yy = y + move_xy[MovDir[x][y]].dy;
7277
7278     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7279       MovDir[x][y] = old_move_dir;
7280
7281     MovDelay[x][y] = 0;
7282   }
7283   else if (element == EL_MOLE)
7284   {
7285     boolean can_move_on =
7286       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7287                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7288                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7289     if (!can_move_on)
7290     {
7291       boolean can_turn_left =
7292         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7293                               IS_AMOEBOID(Feld[left_x][left_y])));
7294
7295       boolean can_turn_right =
7296         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7297                               IS_AMOEBOID(Feld[right_x][right_y])));
7298
7299       if (can_turn_left && can_turn_right)
7300         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7301       else if (can_turn_left)
7302         MovDir[x][y] = left_dir;
7303       else
7304         MovDir[x][y] = right_dir;
7305     }
7306
7307     if (MovDir[x][y] != old_move_dir)
7308       MovDelay[x][y] = 9;
7309   }
7310   else if (element == EL_BALLOON)
7311   {
7312     MovDir[x][y] = game.wind_direction;
7313     MovDelay[x][y] = 0;
7314   }
7315   else if (element == EL_SPRING)
7316   {
7317 #if USE_NEW_SPRING_BUMPER
7318     if (MovDir[x][y] & MV_HORIZONTAL)
7319     {
7320       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7321           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7322       {
7323         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7324         ResetGfxAnimation(move_x, move_y);
7325         TEST_DrawLevelField(move_x, move_y);
7326
7327         MovDir[x][y] = back_dir;
7328       }
7329       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7330                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7331         MovDir[x][y] = MV_NONE;
7332     }
7333 #else
7334     if (MovDir[x][y] & MV_HORIZONTAL &&
7335         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7336          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7337       MovDir[x][y] = MV_NONE;
7338 #endif
7339
7340     MovDelay[x][y] = 0;
7341   }
7342   else if (element == EL_ROBOT ||
7343            element == EL_SATELLITE ||
7344            element == EL_PENGUIN ||
7345            element == EL_EMC_ANDROID)
7346   {
7347     int attr_x = -1, attr_y = -1;
7348
7349     if (AllPlayersGone)
7350     {
7351       attr_x = ExitX;
7352       attr_y = ExitY;
7353     }
7354     else
7355     {
7356       int i;
7357
7358       for (i = 0; i < MAX_PLAYERS; i++)
7359       {
7360         struct PlayerInfo *player = &stored_player[i];
7361         int jx = player->jx, jy = player->jy;
7362
7363         if (!player->active)
7364           continue;
7365
7366         if (attr_x == -1 ||
7367             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7368         {
7369           attr_x = jx;
7370           attr_y = jy;
7371         }
7372       }
7373     }
7374
7375     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7376         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7377          game.engine_version < VERSION_IDENT(3,1,0,0)))
7378     {
7379       attr_x = ZX;
7380       attr_y = ZY;
7381     }
7382
7383     if (element == EL_PENGUIN)
7384     {
7385       int i;
7386       static int xy[4][2] =
7387       {
7388         { 0, -1 },
7389         { -1, 0 },
7390         { +1, 0 },
7391         { 0, +1 }
7392       };
7393
7394       for (i = 0; i < NUM_DIRECTIONS; i++)
7395       {
7396         int ex = x + xy[i][0];
7397         int ey = y + xy[i][1];
7398
7399         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7400                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7401                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7402                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7403         {
7404           attr_x = ex;
7405           attr_y = ey;
7406           break;
7407         }
7408       }
7409     }
7410
7411     MovDir[x][y] = MV_NONE;
7412     if (attr_x < x)
7413       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7414     else if (attr_x > x)
7415       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7416     if (attr_y < y)
7417       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7418     else if (attr_y > y)
7419       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7420
7421     if (element == EL_ROBOT)
7422     {
7423       int newx, newy;
7424
7425       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7426         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7427       Moving2Blocked(x, y, &newx, &newy);
7428
7429       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7430         MovDelay[x][y] = 8 + 8 * !RND(3);
7431       else
7432         MovDelay[x][y] = 16;
7433     }
7434     else if (element == EL_PENGUIN)
7435     {
7436       int newx, newy;
7437
7438       MovDelay[x][y] = 1;
7439
7440       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7441       {
7442         boolean first_horiz = RND(2);
7443         int new_move_dir = MovDir[x][y];
7444
7445         MovDir[x][y] =
7446           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7447         Moving2Blocked(x, y, &newx, &newy);
7448
7449         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7450           return;
7451
7452         MovDir[x][y] =
7453           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7454         Moving2Blocked(x, y, &newx, &newy);
7455
7456         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7457           return;
7458
7459         MovDir[x][y] = old_move_dir;
7460         return;
7461       }
7462     }
7463     else if (element == EL_SATELLITE)
7464     {
7465       int newx, newy;
7466
7467       MovDelay[x][y] = 1;
7468
7469       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7470       {
7471         boolean first_horiz = RND(2);
7472         int new_move_dir = MovDir[x][y];
7473
7474         MovDir[x][y] =
7475           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7476         Moving2Blocked(x, y, &newx, &newy);
7477
7478         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7479           return;
7480
7481         MovDir[x][y] =
7482           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7483         Moving2Blocked(x, y, &newx, &newy);
7484
7485         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7486           return;
7487
7488         MovDir[x][y] = old_move_dir;
7489         return;
7490       }
7491     }
7492     else if (element == EL_EMC_ANDROID)
7493     {
7494       static int check_pos[16] =
7495       {
7496         -1,             /*  0 => (invalid)          */
7497         7,              /*  1 => MV_LEFT            */
7498         3,              /*  2 => MV_RIGHT           */
7499         -1,             /*  3 => (invalid)          */
7500         1,              /*  4 =>            MV_UP   */
7501         0,              /*  5 => MV_LEFT  | MV_UP   */
7502         2,              /*  6 => MV_RIGHT | MV_UP   */
7503         -1,             /*  7 => (invalid)          */
7504         5,              /*  8 =>            MV_DOWN */
7505         6,              /*  9 => MV_LEFT  | MV_DOWN */
7506         4,              /* 10 => MV_RIGHT | MV_DOWN */
7507         -1,             /* 11 => (invalid)          */
7508         -1,             /* 12 => (invalid)          */
7509         -1,             /* 13 => (invalid)          */
7510         -1,             /* 14 => (invalid)          */
7511         -1,             /* 15 => (invalid)          */
7512       };
7513       static struct
7514       {
7515         int dx, dy;
7516         int dir;
7517       } check_xy[8] =
7518       {
7519         { -1, -1,       MV_LEFT  | MV_UP   },
7520         {  0, -1,                  MV_UP   },
7521         { +1, -1,       MV_RIGHT | MV_UP   },
7522         { +1,  0,       MV_RIGHT           },
7523         { +1, +1,       MV_RIGHT | MV_DOWN },
7524         {  0, +1,                  MV_DOWN },
7525         { -1, +1,       MV_LEFT  | MV_DOWN },
7526         { -1,  0,       MV_LEFT            },
7527       };
7528       int start_pos, check_order;
7529       boolean can_clone = FALSE;
7530       int i;
7531
7532       /* check if there is any free field around current position */
7533       for (i = 0; i < 8; i++)
7534       {
7535         int newx = x + check_xy[i].dx;
7536         int newy = y + check_xy[i].dy;
7537
7538         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7539         {
7540           can_clone = TRUE;
7541
7542           break;
7543         }
7544       }
7545
7546       if (can_clone)            /* randomly find an element to clone */
7547       {
7548         can_clone = FALSE;
7549
7550         start_pos = check_pos[RND(8)];
7551         check_order = (RND(2) ? -1 : +1);
7552
7553         for (i = 0; i < 8; i++)
7554         {
7555           int pos_raw = start_pos + i * check_order;
7556           int pos = (pos_raw + 8) % 8;
7557           int newx = x + check_xy[pos].dx;
7558           int newy = y + check_xy[pos].dy;
7559
7560           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7561           {
7562             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7563             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7564
7565             Store[x][y] = Feld[newx][newy];
7566
7567             can_clone = TRUE;
7568
7569             break;
7570           }
7571         }
7572       }
7573
7574       if (can_clone)            /* randomly find a direction to move */
7575       {
7576         can_clone = FALSE;
7577
7578         start_pos = check_pos[RND(8)];
7579         check_order = (RND(2) ? -1 : +1);
7580
7581         for (i = 0; i < 8; i++)
7582         {
7583           int pos_raw = start_pos + i * check_order;
7584           int pos = (pos_raw + 8) % 8;
7585           int newx = x + check_xy[pos].dx;
7586           int newy = y + check_xy[pos].dy;
7587           int new_move_dir = check_xy[pos].dir;
7588
7589           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7590           {
7591             MovDir[x][y] = new_move_dir;
7592             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7593
7594             can_clone = TRUE;
7595
7596             break;
7597           }
7598         }
7599       }
7600
7601       if (can_clone)            /* cloning and moving successful */
7602         return;
7603
7604       /* cannot clone -- try to move towards player */
7605
7606       start_pos = check_pos[MovDir[x][y] & 0x0f];
7607       check_order = (RND(2) ? -1 : +1);
7608
7609       for (i = 0; i < 3; i++)
7610       {
7611         /* first check start_pos, then previous/next or (next/previous) pos */
7612         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7613         int pos = (pos_raw + 8) % 8;
7614         int newx = x + check_xy[pos].dx;
7615         int newy = y + check_xy[pos].dy;
7616         int new_move_dir = check_xy[pos].dir;
7617
7618         if (IS_PLAYER(newx, newy))
7619           break;
7620
7621         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7622         {
7623           MovDir[x][y] = new_move_dir;
7624           MovDelay[x][y] = level.android_move_time * 8 + 1;
7625
7626           break;
7627         }
7628       }
7629     }
7630   }
7631   else if (move_pattern == MV_TURNING_LEFT ||
7632            move_pattern == MV_TURNING_RIGHT ||
7633            move_pattern == MV_TURNING_LEFT_RIGHT ||
7634            move_pattern == MV_TURNING_RIGHT_LEFT ||
7635            move_pattern == MV_TURNING_RANDOM ||
7636            move_pattern == MV_ALL_DIRECTIONS)
7637   {
7638     boolean can_turn_left =
7639       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7640     boolean can_turn_right =
7641       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7642
7643     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7644       return;
7645
7646     if (move_pattern == MV_TURNING_LEFT)
7647       MovDir[x][y] = left_dir;
7648     else if (move_pattern == MV_TURNING_RIGHT)
7649       MovDir[x][y] = right_dir;
7650     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7651       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7652     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7653       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7654     else if (move_pattern == MV_TURNING_RANDOM)
7655       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7656                       can_turn_right && !can_turn_left ? right_dir :
7657                       RND(2) ? left_dir : right_dir);
7658     else if (can_turn_left && can_turn_right)
7659       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7660     else if (can_turn_left)
7661       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7662     else if (can_turn_right)
7663       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7664     else
7665       MovDir[x][y] = back_dir;
7666
7667     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7668   }
7669   else if (move_pattern == MV_HORIZONTAL ||
7670            move_pattern == MV_VERTICAL)
7671   {
7672     if (move_pattern & old_move_dir)
7673       MovDir[x][y] = back_dir;
7674     else if (move_pattern == MV_HORIZONTAL)
7675       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7676     else if (move_pattern == MV_VERTICAL)
7677       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7678
7679     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7680   }
7681   else if (move_pattern & MV_ANY_DIRECTION)
7682   {
7683     MovDir[x][y] = move_pattern;
7684     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7685   }
7686   else if (move_pattern & MV_WIND_DIRECTION)
7687   {
7688     MovDir[x][y] = game.wind_direction;
7689     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7690   }
7691   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7692   {
7693     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7694       MovDir[x][y] = left_dir;
7695     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7696       MovDir[x][y] = right_dir;
7697
7698     if (MovDir[x][y] != old_move_dir)
7699       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7700   }
7701   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7702   {
7703     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7704       MovDir[x][y] = right_dir;
7705     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7706       MovDir[x][y] = left_dir;
7707
7708     if (MovDir[x][y] != old_move_dir)
7709       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7710   }
7711   else if (move_pattern == MV_TOWARDS_PLAYER ||
7712            move_pattern == MV_AWAY_FROM_PLAYER)
7713   {
7714     int attr_x = -1, attr_y = -1;
7715     int newx, newy;
7716     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7717
7718     if (AllPlayersGone)
7719     {
7720       attr_x = ExitX;
7721       attr_y = ExitY;
7722     }
7723     else
7724     {
7725       int i;
7726
7727       for (i = 0; i < MAX_PLAYERS; i++)
7728       {
7729         struct PlayerInfo *player = &stored_player[i];
7730         int jx = player->jx, jy = player->jy;
7731
7732         if (!player->active)
7733           continue;
7734
7735         if (attr_x == -1 ||
7736             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7737         {
7738           attr_x = jx;
7739           attr_y = jy;
7740         }
7741       }
7742     }
7743
7744     MovDir[x][y] = MV_NONE;
7745     if (attr_x < x)
7746       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7747     else if (attr_x > x)
7748       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7749     if (attr_y < y)
7750       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7751     else if (attr_y > y)
7752       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7753
7754     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7755
7756     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7757     {
7758       boolean first_horiz = RND(2);
7759       int new_move_dir = MovDir[x][y];
7760
7761       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7762       {
7763         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7764         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7765
7766         return;
7767       }
7768
7769       MovDir[x][y] =
7770         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7771       Moving2Blocked(x, y, &newx, &newy);
7772
7773       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7774         return;
7775
7776       MovDir[x][y] =
7777         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7778       Moving2Blocked(x, y, &newx, &newy);
7779
7780       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7781         return;
7782
7783       MovDir[x][y] = old_move_dir;
7784     }
7785   }
7786   else if (move_pattern == MV_WHEN_PUSHED ||
7787            move_pattern == MV_WHEN_DROPPED)
7788   {
7789     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7790       MovDir[x][y] = MV_NONE;
7791
7792     MovDelay[x][y] = 0;
7793   }
7794   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7795   {
7796     static int test_xy[7][2] =
7797     {
7798       { 0, -1 },
7799       { -1, 0 },
7800       { +1, 0 },
7801       { 0, +1 },
7802       { 0, -1 },
7803       { -1, 0 },
7804       { +1, 0 },
7805     };
7806     static int test_dir[7] =
7807     {
7808       MV_UP,
7809       MV_LEFT,
7810       MV_RIGHT,
7811       MV_DOWN,
7812       MV_UP,
7813       MV_LEFT,
7814       MV_RIGHT,
7815     };
7816     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7817     int move_preference = -1000000;     /* start with very low preference */
7818     int new_move_dir = MV_NONE;
7819     int start_test = RND(4);
7820     int i;
7821
7822     for (i = 0; i < NUM_DIRECTIONS; i++)
7823     {
7824       int move_dir = test_dir[start_test + i];
7825       int move_dir_preference;
7826
7827       xx = x + test_xy[start_test + i][0];
7828       yy = y + test_xy[start_test + i][1];
7829
7830       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7831           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7832       {
7833         new_move_dir = move_dir;
7834
7835         break;
7836       }
7837
7838       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7839         continue;
7840
7841       move_dir_preference = -1 * RunnerVisit[xx][yy];
7842       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7843         move_dir_preference = PlayerVisit[xx][yy];
7844
7845       if (move_dir_preference > move_preference)
7846       {
7847         /* prefer field that has not been visited for the longest time */
7848         move_preference = move_dir_preference;
7849         new_move_dir = move_dir;
7850       }
7851       else if (move_dir_preference == move_preference &&
7852                move_dir == old_move_dir)
7853       {
7854         /* prefer last direction when all directions are preferred equally */
7855         move_preference = move_dir_preference;
7856         new_move_dir = move_dir;
7857       }
7858     }
7859
7860     MovDir[x][y] = new_move_dir;
7861     if (old_move_dir != new_move_dir)
7862       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7863   }
7864 }
7865
7866 static void TurnRound(int x, int y)
7867 {
7868   int direction = MovDir[x][y];
7869
7870   TurnRoundExt(x, y);
7871
7872   GfxDir[x][y] = MovDir[x][y];
7873
7874   if (direction != MovDir[x][y])
7875     GfxFrame[x][y] = 0;
7876
7877   if (MovDelay[x][y])
7878     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7879
7880   ResetGfxFrame(x, y, FALSE);
7881 }
7882
7883 static boolean JustBeingPushed(int x, int y)
7884 {
7885   int i;
7886
7887   for (i = 0; i < MAX_PLAYERS; i++)
7888   {
7889     struct PlayerInfo *player = &stored_player[i];
7890
7891     if (player->active && player->is_pushing && player->MovPos)
7892     {
7893       int next_jx = player->jx + (player->jx - player->last_jx);
7894       int next_jy = player->jy + (player->jy - player->last_jy);
7895
7896       if (x == next_jx && y == next_jy)
7897         return TRUE;
7898     }
7899   }
7900
7901   return FALSE;
7902 }
7903
7904 void StartMoving(int x, int y)
7905 {
7906   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7907   int element = Feld[x][y];
7908
7909   if (Stop[x][y])
7910     return;
7911
7912   if (MovDelay[x][y] == 0)
7913     GfxAction[x][y] = ACTION_DEFAULT;
7914
7915   if (CAN_FALL(element) && y < lev_fieldy - 1)
7916   {
7917     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7918         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7919       if (JustBeingPushed(x, y))
7920         return;
7921
7922     if (element == EL_QUICKSAND_FULL)
7923     {
7924       if (IS_FREE(x, y + 1))
7925       {
7926         InitMovingField(x, y, MV_DOWN);
7927         started_moving = TRUE;
7928
7929         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7930 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7931         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7932           Store[x][y] = EL_ROCK;
7933 #else
7934         Store[x][y] = EL_ROCK;
7935 #endif
7936
7937         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7938       }
7939       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7940       {
7941         if (!MovDelay[x][y])
7942         {
7943           MovDelay[x][y] = TILEY + 1;
7944
7945           ResetGfxAnimation(x, y);
7946           ResetGfxAnimation(x, y + 1);
7947         }
7948
7949         if (MovDelay[x][y])
7950         {
7951           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7953
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Feld[x][y] = EL_QUICKSAND_EMPTY;
7960         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7961         Store[x][y + 1] = Store[x][y];
7962         Store[x][y] = 0;
7963
7964         PlayLevelSoundAction(x, y, ACTION_FILLING);
7965       }
7966       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7967       {
7968         if (!MovDelay[x][y])
7969         {
7970           MovDelay[x][y] = TILEY + 1;
7971
7972           ResetGfxAnimation(x, y);
7973           ResetGfxAnimation(x, y + 1);
7974         }
7975
7976         if (MovDelay[x][y])
7977         {
7978           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7979           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7980
7981           MovDelay[x][y]--;
7982           if (MovDelay[x][y])
7983             return;
7984         }
7985
7986         Feld[x][y] = EL_QUICKSAND_EMPTY;
7987         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7988         Store[x][y + 1] = Store[x][y];
7989         Store[x][y] = 0;
7990
7991         PlayLevelSoundAction(x, y, ACTION_FILLING);
7992       }
7993     }
7994     else if (element == EL_QUICKSAND_FAST_FULL)
7995     {
7996       if (IS_FREE(x, y + 1))
7997       {
7998         InitMovingField(x, y, MV_DOWN);
7999         started_moving = TRUE;
8000
8001         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8002 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8003         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8004           Store[x][y] = EL_ROCK;
8005 #else
8006         Store[x][y] = EL_ROCK;
8007 #endif
8008
8009         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8010       }
8011       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8012       {
8013         if (!MovDelay[x][y])
8014         {
8015           MovDelay[x][y] = TILEY + 1;
8016
8017           ResetGfxAnimation(x, y);
8018           ResetGfxAnimation(x, y + 1);
8019         }
8020
8021         if (MovDelay[x][y])
8022         {
8023           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8025
8026           MovDelay[x][y]--;
8027           if (MovDelay[x][y])
8028             return;
8029         }
8030
8031         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8033         Store[x][y + 1] = Store[x][y];
8034         Store[x][y] = 0;
8035
8036         PlayLevelSoundAction(x, y, ACTION_FILLING);
8037       }
8038       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8039       {
8040         if (!MovDelay[x][y])
8041         {
8042           MovDelay[x][y] = TILEY + 1;
8043
8044           ResetGfxAnimation(x, y);
8045           ResetGfxAnimation(x, y + 1);
8046         }
8047
8048         if (MovDelay[x][y])
8049         {
8050           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8051           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8052
8053           MovDelay[x][y]--;
8054           if (MovDelay[x][y])
8055             return;
8056         }
8057
8058         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8059         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8060         Store[x][y + 1] = Store[x][y];
8061         Store[x][y] = 0;
8062
8063         PlayLevelSoundAction(x, y, ACTION_FILLING);
8064       }
8065     }
8066     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8067              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8068     {
8069       InitMovingField(x, y, MV_DOWN);
8070       started_moving = TRUE;
8071
8072       Feld[x][y] = EL_QUICKSAND_FILLING;
8073       Store[x][y] = element;
8074
8075       PlayLevelSoundAction(x, y, ACTION_FILLING);
8076     }
8077     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8078              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8079     {
8080       InitMovingField(x, y, MV_DOWN);
8081       started_moving = TRUE;
8082
8083       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8084       Store[x][y] = element;
8085
8086       PlayLevelSoundAction(x, y, ACTION_FILLING);
8087     }
8088     else if (element == EL_MAGIC_WALL_FULL)
8089     {
8090       if (IS_FREE(x, y + 1))
8091       {
8092         InitMovingField(x, y, MV_DOWN);
8093         started_moving = TRUE;
8094
8095         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8096         Store[x][y] = EL_CHANGED(Store[x][y]);
8097       }
8098       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8099       {
8100         if (!MovDelay[x][y])
8101           MovDelay[x][y] = TILEY/4 + 1;
8102
8103         if (MovDelay[x][y])
8104         {
8105           MovDelay[x][y]--;
8106           if (MovDelay[x][y])
8107             return;
8108         }
8109
8110         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8111         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8112         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8113         Store[x][y] = 0;
8114       }
8115     }
8116     else if (element == EL_BD_MAGIC_WALL_FULL)
8117     {
8118       if (IS_FREE(x, y + 1))
8119       {
8120         InitMovingField(x, y, MV_DOWN);
8121         started_moving = TRUE;
8122
8123         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8124         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8125       }
8126       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8127       {
8128         if (!MovDelay[x][y])
8129           MovDelay[x][y] = TILEY/4 + 1;
8130
8131         if (MovDelay[x][y])
8132         {
8133           MovDelay[x][y]--;
8134           if (MovDelay[x][y])
8135             return;
8136         }
8137
8138         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8139         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8140         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8141         Store[x][y] = 0;
8142       }
8143     }
8144     else if (element == EL_DC_MAGIC_WALL_FULL)
8145     {
8146       if (IS_FREE(x, y + 1))
8147       {
8148         InitMovingField(x, y, MV_DOWN);
8149         started_moving = TRUE;
8150
8151         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8152         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8153       }
8154       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8155       {
8156         if (!MovDelay[x][y])
8157           MovDelay[x][y] = TILEY/4 + 1;
8158
8159         if (MovDelay[x][y])
8160         {
8161           MovDelay[x][y]--;
8162           if (MovDelay[x][y])
8163             return;
8164         }
8165
8166         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8167         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8168         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8169         Store[x][y] = 0;
8170       }
8171     }
8172     else if ((CAN_PASS_MAGIC_WALL(element) &&
8173               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8174                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8175              (CAN_PASS_DC_MAGIC_WALL(element) &&
8176               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8177
8178     {
8179       InitMovingField(x, y, MV_DOWN);
8180       started_moving = TRUE;
8181
8182       Feld[x][y] =
8183         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8184          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8185          EL_DC_MAGIC_WALL_FILLING);
8186       Store[x][y] = element;
8187     }
8188     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8189     {
8190       SplashAcid(x, y + 1);
8191
8192       InitMovingField(x, y, MV_DOWN);
8193       started_moving = TRUE;
8194
8195       Store[x][y] = EL_ACID;
8196     }
8197     else if (
8198 #if USE_FIX_IMPACT_COLLISION
8199              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8200               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8201 #else
8202              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8203               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8204 #endif
8205              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8206               CAN_FALL(element) && WasJustFalling[x][y] &&
8207               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8208
8209              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8210               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8211               (Feld[x][y + 1] == EL_BLOCKED)))
8212     {
8213       /* this is needed for a special case not covered by calling "Impact()"
8214          from "ContinueMoving()": if an element moves to a tile directly below
8215          another element which was just falling on that tile (which was empty
8216          in the previous frame), the falling element above would just stop
8217          instead of smashing the element below (in previous version, the above
8218          element was just checked for "moving" instead of "falling", resulting
8219          in incorrect smashes caused by horizontal movement of the above
8220          element; also, the case of the player being the element to smash was
8221          simply not covered here... :-/ ) */
8222
8223       CheckCollision[x][y] = 0;
8224       CheckImpact[x][y] = 0;
8225
8226       Impact(x, y);
8227     }
8228     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8229     {
8230       if (MovDir[x][y] == MV_NONE)
8231       {
8232         InitMovingField(x, y, MV_DOWN);
8233         started_moving = TRUE;
8234       }
8235     }
8236     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8237     {
8238       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8239         MovDir[x][y] = MV_DOWN;
8240
8241       InitMovingField(x, y, MV_DOWN);
8242       started_moving = TRUE;
8243     }
8244     else if (element == EL_AMOEBA_DROP)
8245     {
8246       Feld[x][y] = EL_AMOEBA_GROWING;
8247       Store[x][y] = EL_AMOEBA_WET;
8248     }
8249     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8250               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8251              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8252              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8253     {
8254       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8255                                 (IS_FREE(x - 1, y + 1) ||
8256                                  Feld[x - 1][y + 1] == EL_ACID));
8257       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8258                                 (IS_FREE(x + 1, y + 1) ||
8259                                  Feld[x + 1][y + 1] == EL_ACID));
8260       boolean can_fall_any  = (can_fall_left || can_fall_right);
8261       boolean can_fall_both = (can_fall_left && can_fall_right);
8262       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8263
8264 #if USE_NEW_ALL_SLIPPERY
8265       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8266       {
8267         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8268           can_fall_right = FALSE;
8269         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8270           can_fall_left = FALSE;
8271         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8272           can_fall_right = FALSE;
8273         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8274           can_fall_left = FALSE;
8275
8276         can_fall_any  = (can_fall_left || can_fall_right);
8277         can_fall_both = FALSE;
8278       }
8279 #else
8280       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8281       {
8282         if (slippery_type == SLIPPERY_ONLY_LEFT)
8283           can_fall_right = FALSE;
8284         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8285           can_fall_left = FALSE;
8286         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8287           can_fall_right = FALSE;
8288         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8289           can_fall_left = FALSE;
8290
8291         can_fall_any  = (can_fall_left || can_fall_right);
8292         can_fall_both = (can_fall_left && can_fall_right);
8293       }
8294 #endif
8295
8296 #if USE_NEW_ALL_SLIPPERY
8297 #else
8298 #if USE_NEW_SP_SLIPPERY
8299       /* !!! better use the same properties as for custom elements here !!! */
8300       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8301                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8302       {
8303         can_fall_right = FALSE;         /* slip down on left side */
8304         can_fall_both = FALSE;
8305       }
8306 #endif
8307 #endif
8308
8309 #if USE_NEW_ALL_SLIPPERY
8310       if (can_fall_both)
8311       {
8312         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8313           can_fall_right = FALSE;       /* slip down on left side */
8314         else
8315           can_fall_left = !(can_fall_right = RND(2));
8316
8317         can_fall_both = FALSE;
8318       }
8319 #else
8320       if (can_fall_both)
8321       {
8322         if (game.emulation == EMU_BOULDERDASH ||
8323             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8324           can_fall_right = FALSE;       /* slip down on left side */
8325         else
8326           can_fall_left = !(can_fall_right = RND(2));
8327
8328         can_fall_both = FALSE;
8329       }
8330 #endif
8331
8332       if (can_fall_any)
8333       {
8334         /* if not determined otherwise, prefer left side for slipping down */
8335         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8336         started_moving = TRUE;
8337       }
8338     }
8339 #if 0
8340     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8341 #else
8342     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8343 #endif
8344     {
8345       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8346       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8347       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8348       int belt_dir = game.belt_dir[belt_nr];
8349
8350       if ((belt_dir == MV_LEFT  && left_is_free) ||
8351           (belt_dir == MV_RIGHT && right_is_free))
8352       {
8353         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8354
8355         InitMovingField(x, y, belt_dir);
8356         started_moving = TRUE;
8357
8358         Pushed[x][y] = TRUE;
8359         Pushed[nextx][y] = TRUE;
8360
8361         GfxAction[x][y] = ACTION_DEFAULT;
8362       }
8363       else
8364       {
8365         MovDir[x][y] = 0;       /* if element was moving, stop it */
8366       }
8367     }
8368   }
8369
8370   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8371 #if 0
8372   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8373 #else
8374   if (CAN_MOVE(element) && !started_moving)
8375 #endif
8376   {
8377     int move_pattern = element_info[element].move_pattern;
8378     int newx, newy;
8379
8380 #if 0
8381 #if DEBUG
8382     if (MovDir[x][y] == MV_NONE)
8383     {
8384       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8385              x, y, element, element_info[element].token_name);
8386       printf("StartMoving(): This should never happen!\n");
8387     }
8388 #endif
8389 #endif
8390
8391     Moving2Blocked(x, y, &newx, &newy);
8392
8393     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8394       return;
8395
8396     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8397         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8398     {
8399       WasJustMoving[x][y] = 0;
8400       CheckCollision[x][y] = 0;
8401
8402       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8403
8404       if (Feld[x][y] != element)        /* element has changed */
8405         return;
8406     }
8407
8408     if (!MovDelay[x][y])        /* start new movement phase */
8409     {
8410       /* all objects that can change their move direction after each step
8411          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8412
8413       if (element != EL_YAMYAM &&
8414           element != EL_DARK_YAMYAM &&
8415           element != EL_PACMAN &&
8416           !(move_pattern & MV_ANY_DIRECTION) &&
8417           move_pattern != MV_TURNING_LEFT &&
8418           move_pattern != MV_TURNING_RIGHT &&
8419           move_pattern != MV_TURNING_LEFT_RIGHT &&
8420           move_pattern != MV_TURNING_RIGHT_LEFT &&
8421           move_pattern != MV_TURNING_RANDOM)
8422       {
8423         TurnRound(x, y);
8424
8425         if (MovDelay[x][y] && (element == EL_BUG ||
8426                                element == EL_SPACESHIP ||
8427                                element == EL_SP_SNIKSNAK ||
8428                                element == EL_SP_ELECTRON ||
8429                                element == EL_MOLE))
8430           TEST_DrawLevelField(x, y);
8431       }
8432     }
8433
8434     if (MovDelay[x][y])         /* wait some time before next movement */
8435     {
8436       MovDelay[x][y]--;
8437
8438       if (element == EL_ROBOT ||
8439           element == EL_YAMYAM ||
8440           element == EL_DARK_YAMYAM)
8441       {
8442         DrawLevelElementAnimationIfNeeded(x, y, element);
8443         PlayLevelSoundAction(x, y, ACTION_WAITING);
8444       }
8445       else if (element == EL_SP_ELECTRON)
8446         DrawLevelElementAnimationIfNeeded(x, y, element);
8447       else if (element == EL_DRAGON)
8448       {
8449         int i;
8450         int dir = MovDir[x][y];
8451         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8452         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8453         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8454                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8455                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8456                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8457         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8458
8459         GfxAction[x][y] = ACTION_ATTACKING;
8460
8461         if (IS_PLAYER(x, y))
8462           DrawPlayerField(x, y);
8463         else
8464           TEST_DrawLevelField(x, y);
8465
8466         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8467
8468         for (i = 1; i <= 3; i++)
8469         {
8470           int xx = x + i * dx;
8471           int yy = y + i * dy;
8472           int sx = SCREENX(xx);
8473           int sy = SCREENY(yy);
8474           int flame_graphic = graphic + (i - 1);
8475
8476           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8477             break;
8478
8479           if (MovDelay[x][y])
8480           {
8481             int flamed = MovingOrBlocked2Element(xx, yy);
8482
8483             /* !!! */
8484 #if 0
8485             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8486               Bang(xx, yy);
8487             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8488               RemoveMovingField(xx, yy);
8489             else
8490               RemoveField(xx, yy);
8491 #else
8492             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8493               Bang(xx, yy);
8494             else
8495               RemoveMovingField(xx, yy);
8496 #endif
8497
8498             ChangeDelay[xx][yy] = 0;
8499
8500             Feld[xx][yy] = EL_FLAMES;
8501
8502             if (IN_SCR_FIELD(sx, sy))
8503             {
8504               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8505               DrawGraphic(sx, sy, flame_graphic, frame);
8506             }
8507           }
8508           else
8509           {
8510             if (Feld[xx][yy] == EL_FLAMES)
8511               Feld[xx][yy] = EL_EMPTY;
8512             TEST_DrawLevelField(xx, yy);
8513           }
8514         }
8515       }
8516
8517       if (MovDelay[x][y])       /* element still has to wait some time */
8518       {
8519         PlayLevelSoundAction(x, y, ACTION_WAITING);
8520
8521         return;
8522       }
8523     }
8524
8525     /* now make next step */
8526
8527     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8528
8529     if (DONT_COLLIDE_WITH(element) &&
8530         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8531         !PLAYER_ENEMY_PROTECTED(newx, newy))
8532     {
8533       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8534
8535       return;
8536     }
8537
8538     else if (CAN_MOVE_INTO_ACID(element) &&
8539              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8540              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8541              (MovDir[x][y] == MV_DOWN ||
8542               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8543     {
8544       SplashAcid(newx, newy);
8545       Store[x][y] = EL_ACID;
8546     }
8547     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8548     {
8549       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8550           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8551           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8552           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8553       {
8554         RemoveField(x, y);
8555         TEST_DrawLevelField(x, y);
8556
8557         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8558         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8559           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8560
8561         local_player->friends_still_needed--;
8562         if (!local_player->friends_still_needed &&
8563             !local_player->GameOver && AllPlayersGone)
8564           PlayerWins(local_player);
8565
8566         return;
8567       }
8568       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8569       {
8570         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8571           TEST_DrawLevelField(newx, newy);
8572         else
8573           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8574       }
8575       else if (!IS_FREE(newx, newy))
8576       {
8577         GfxAction[x][y] = ACTION_WAITING;
8578
8579         if (IS_PLAYER(x, y))
8580           DrawPlayerField(x, y);
8581         else
8582           TEST_DrawLevelField(x, y);
8583
8584         return;
8585       }
8586     }
8587     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8588     {
8589       if (IS_FOOD_PIG(Feld[newx][newy]))
8590       {
8591         if (IS_MOVING(newx, newy))
8592           RemoveMovingField(newx, newy);
8593         else
8594         {
8595           Feld[newx][newy] = EL_EMPTY;
8596           TEST_DrawLevelField(newx, newy);
8597         }
8598
8599         PlayLevelSound(x, y, SND_PIG_DIGGING);
8600       }
8601       else if (!IS_FREE(newx, newy))
8602       {
8603         if (IS_PLAYER(x, y))
8604           DrawPlayerField(x, y);
8605         else
8606           TEST_DrawLevelField(x, y);
8607
8608         return;
8609       }
8610     }
8611     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8612     {
8613       if (Store[x][y] != EL_EMPTY)
8614       {
8615         boolean can_clone = FALSE;
8616         int xx, yy;
8617
8618         /* check if element to clone is still there */
8619         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8620         {
8621           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8622           {
8623             can_clone = TRUE;
8624
8625             break;
8626           }
8627         }
8628
8629         /* cannot clone or target field not free anymore -- do not clone */
8630         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8631           Store[x][y] = EL_EMPTY;
8632       }
8633
8634       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8635       {
8636         if (IS_MV_DIAGONAL(MovDir[x][y]))
8637         {
8638           int diagonal_move_dir = MovDir[x][y];
8639           int stored = Store[x][y];
8640           int change_delay = 8;
8641           int graphic;
8642
8643           /* android is moving diagonally */
8644
8645           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8646
8647           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8648           GfxElement[x][y] = EL_EMC_ANDROID;
8649           GfxAction[x][y] = ACTION_SHRINKING;
8650           GfxDir[x][y] = diagonal_move_dir;
8651           ChangeDelay[x][y] = change_delay;
8652
8653           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8654                                    GfxDir[x][y]);
8655
8656           DrawLevelGraphicAnimation(x, y, graphic);
8657           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8658
8659           if (Feld[newx][newy] == EL_ACID)
8660           {
8661             SplashAcid(newx, newy);
8662
8663             return;
8664           }
8665
8666           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8667
8668           Store[newx][newy] = EL_EMC_ANDROID;
8669           GfxElement[newx][newy] = EL_EMC_ANDROID;
8670           GfxAction[newx][newy] = ACTION_GROWING;
8671           GfxDir[newx][newy] = diagonal_move_dir;
8672           ChangeDelay[newx][newy] = change_delay;
8673
8674           graphic = el_act_dir2img(GfxElement[newx][newy],
8675                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8676
8677           DrawLevelGraphicAnimation(newx, newy, graphic);
8678           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8679
8680           return;
8681         }
8682         else
8683         {
8684           Feld[newx][newy] = EL_EMPTY;
8685           TEST_DrawLevelField(newx, newy);
8686
8687           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8688         }
8689       }
8690       else if (!IS_FREE(newx, newy))
8691       {
8692 #if 0
8693         if (IS_PLAYER(x, y))
8694           DrawPlayerField(x, y);
8695         else
8696           TEST_DrawLevelField(x, y);
8697 #endif
8698
8699         return;
8700       }
8701     }
8702     else if (IS_CUSTOM_ELEMENT(element) &&
8703              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8704     {
8705 #if 1
8706       if (!DigFieldByCE(newx, newy, element))
8707         return;
8708 #else
8709       int new_element = Feld[newx][newy];
8710
8711       if (!IS_FREE(newx, newy))
8712       {
8713         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8714                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8715                       ACTION_BREAKING);
8716
8717         /* no element can dig solid indestructible elements */
8718         if (IS_INDESTRUCTIBLE(new_element) &&
8719             !IS_DIGGABLE(new_element) &&
8720             !IS_COLLECTIBLE(new_element))
8721           return;
8722
8723         if (AmoebaNr[newx][newy] &&
8724             (new_element == EL_AMOEBA_FULL ||
8725              new_element == EL_BD_AMOEBA ||
8726              new_element == EL_AMOEBA_GROWING))
8727         {
8728           AmoebaCnt[AmoebaNr[newx][newy]]--;
8729           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8730         }
8731
8732         if (IS_MOVING(newx, newy))
8733           RemoveMovingField(newx, newy);
8734         else
8735         {
8736           RemoveField(newx, newy);
8737           TEST_DrawLevelField(newx, newy);
8738         }
8739
8740         /* if digged element was about to explode, prevent the explosion */
8741         ExplodeField[newx][newy] = EX_TYPE_NONE;
8742
8743         PlayLevelSoundAction(x, y, action);
8744       }
8745
8746       Store[newx][newy] = EL_EMPTY;
8747
8748 #if 1
8749       /* this makes it possible to leave the removed element again */
8750       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8751         Store[newx][newy] = new_element;
8752 #else
8753       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8754       {
8755         int move_leave_element = element_info[element].move_leave_element;
8756
8757         /* this makes it possible to leave the removed element again */
8758         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8759                              new_element : move_leave_element);
8760       }
8761 #endif
8762
8763 #endif
8764
8765       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8766       {
8767         RunnerVisit[x][y] = FrameCounter;
8768         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8769       }
8770     }
8771     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8772     {
8773       if (!IS_FREE(newx, newy))
8774       {
8775         if (IS_PLAYER(x, y))
8776           DrawPlayerField(x, y);
8777         else
8778           TEST_DrawLevelField(x, y);
8779
8780         return;
8781       }
8782       else
8783       {
8784         boolean wanna_flame = !RND(10);
8785         int dx = newx - x, dy = newy - y;
8786         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8787         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8788         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8789                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8790         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8791                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8792
8793         if ((wanna_flame ||
8794              IS_CLASSIC_ENEMY(element1) ||
8795              IS_CLASSIC_ENEMY(element2)) &&
8796             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8797             element1 != EL_FLAMES && element2 != EL_FLAMES)
8798         {
8799           ResetGfxAnimation(x, y);
8800           GfxAction[x][y] = ACTION_ATTACKING;
8801
8802           if (IS_PLAYER(x, y))
8803             DrawPlayerField(x, y);
8804           else
8805             TEST_DrawLevelField(x, y);
8806
8807           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8808
8809           MovDelay[x][y] = 50;
8810
8811           /* !!! */
8812 #if 0
8813           RemoveField(newx, newy);
8814 #endif
8815           Feld[newx][newy] = EL_FLAMES;
8816           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8817           {
8818 #if 0
8819             RemoveField(newx1, newy1);
8820 #endif
8821             Feld[newx1][newy1] = EL_FLAMES;
8822           }
8823           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8824           {
8825 #if 0
8826             RemoveField(newx2, newy2);
8827 #endif
8828             Feld[newx2][newy2] = EL_FLAMES;
8829           }
8830
8831           return;
8832         }
8833       }
8834     }
8835     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8836              Feld[newx][newy] == EL_DIAMOND)
8837     {
8838       if (IS_MOVING(newx, newy))
8839         RemoveMovingField(newx, newy);
8840       else
8841       {
8842         Feld[newx][newy] = EL_EMPTY;
8843         TEST_DrawLevelField(newx, newy);
8844       }
8845
8846       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8847     }
8848     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8849              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8850     {
8851       if (AmoebaNr[newx][newy])
8852       {
8853         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8854         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8855             Feld[newx][newy] == EL_BD_AMOEBA)
8856           AmoebaCnt[AmoebaNr[newx][newy]]--;
8857       }
8858
8859 #if 0
8860       /* !!! test !!! */
8861       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8862       {
8863         RemoveMovingField(newx, newy);
8864       }
8865 #else
8866       if (IS_MOVING(newx, newy))
8867       {
8868         RemoveMovingField(newx, newy);
8869       }
8870 #endif
8871       else
8872       {
8873         Feld[newx][newy] = EL_EMPTY;
8874         TEST_DrawLevelField(newx, newy);
8875       }
8876
8877       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8878     }
8879     else if ((element == EL_PACMAN || element == EL_MOLE)
8880              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8881     {
8882       if (AmoebaNr[newx][newy])
8883       {
8884         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8885         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8886             Feld[newx][newy] == EL_BD_AMOEBA)
8887           AmoebaCnt[AmoebaNr[newx][newy]]--;
8888       }
8889
8890       if (element == EL_MOLE)
8891       {
8892         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8893         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8894
8895         ResetGfxAnimation(x, y);
8896         GfxAction[x][y] = ACTION_DIGGING;
8897         TEST_DrawLevelField(x, y);
8898
8899         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8900
8901         return;                         /* wait for shrinking amoeba */
8902       }
8903       else      /* element == EL_PACMAN */
8904       {
8905         Feld[newx][newy] = EL_EMPTY;
8906         TEST_DrawLevelField(newx, newy);
8907         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8908       }
8909     }
8910     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8911              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8912               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8913     {
8914       /* wait for shrinking amoeba to completely disappear */
8915       return;
8916     }
8917     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8918     {
8919       /* object was running against a wall */
8920
8921       TurnRound(x, y);
8922
8923 #if 0
8924       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8925       if (move_pattern & MV_ANY_DIRECTION &&
8926           move_pattern == MovDir[x][y])
8927       {
8928         int blocking_element =
8929           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8930
8931         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8932                                  MovDir[x][y]);
8933
8934         element = Feld[x][y];   /* element might have changed */
8935       }
8936 #endif
8937
8938       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8939         DrawLevelElementAnimation(x, y, element);
8940
8941       if (DONT_TOUCH(element))
8942         TestIfBadThingTouchesPlayer(x, y);
8943
8944       return;
8945     }
8946
8947     InitMovingField(x, y, MovDir[x][y]);
8948
8949     PlayLevelSoundAction(x, y, ACTION_MOVING);
8950   }
8951
8952   if (MovDir[x][y])
8953     ContinueMoving(x, y);
8954 }
8955
8956 void ContinueMoving(int x, int y)
8957 {
8958   int element = Feld[x][y];
8959   struct ElementInfo *ei = &element_info[element];
8960   int direction = MovDir[x][y];
8961   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8962   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8963   int newx = x + dx, newy = y + dy;
8964   int stored = Store[x][y];
8965   int stored_new = Store[newx][newy];
8966   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8967   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8968   boolean last_line = (newy == lev_fieldy - 1);
8969
8970   MovPos[x][y] += getElementMoveStepsize(x, y);
8971
8972   if (pushed_by_player) /* special case: moving object pushed by player */
8973     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8974
8975   if (ABS(MovPos[x][y]) < TILEX)
8976   {
8977 #if 0
8978     int ee = Feld[x][y];
8979     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8980     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8981
8982     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8983            x, y, ABS(MovPos[x][y]),
8984            ee, gg, ff,
8985            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8986 #endif
8987
8988     TEST_DrawLevelField(x, y);
8989
8990     return;     /* element is still moving */
8991   }
8992
8993   /* element reached destination field */
8994
8995   Feld[x][y] = EL_EMPTY;
8996   Feld[newx][newy] = element;
8997   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8998
8999   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9000   {
9001     element = Feld[newx][newy] = EL_ACID;
9002   }
9003   else if (element == EL_MOLE)
9004   {
9005     Feld[x][y] = EL_SAND;
9006
9007     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9008   }
9009   else if (element == EL_QUICKSAND_FILLING)
9010   {
9011     element = Feld[newx][newy] = get_next_element(element);
9012     Store[newx][newy] = Store[x][y];
9013   }
9014   else if (element == EL_QUICKSAND_EMPTYING)
9015   {
9016     Feld[x][y] = get_next_element(element);
9017     element = Feld[newx][newy] = Store[x][y];
9018   }
9019   else if (element == EL_QUICKSAND_FAST_FILLING)
9020   {
9021     element = Feld[newx][newy] = get_next_element(element);
9022     Store[newx][newy] = Store[x][y];
9023   }
9024   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9025   {
9026     Feld[x][y] = get_next_element(element);
9027     element = Feld[newx][newy] = Store[x][y];
9028   }
9029   else if (element == EL_MAGIC_WALL_FILLING)
9030   {
9031     element = Feld[newx][newy] = get_next_element(element);
9032     if (!game.magic_wall_active)
9033       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9034     Store[newx][newy] = Store[x][y];
9035   }
9036   else if (element == EL_MAGIC_WALL_EMPTYING)
9037   {
9038     Feld[x][y] = get_next_element(element);
9039     if (!game.magic_wall_active)
9040       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9041     element = Feld[newx][newy] = Store[x][y];
9042
9043 #if USE_NEW_CUSTOM_VALUE
9044     InitField(newx, newy, FALSE);
9045 #endif
9046   }
9047   else if (element == EL_BD_MAGIC_WALL_FILLING)
9048   {
9049     element = Feld[newx][newy] = get_next_element(element);
9050     if (!game.magic_wall_active)
9051       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9052     Store[newx][newy] = Store[x][y];
9053   }
9054   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9055   {
9056     Feld[x][y] = get_next_element(element);
9057     if (!game.magic_wall_active)
9058       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9059     element = Feld[newx][newy] = Store[x][y];
9060
9061 #if USE_NEW_CUSTOM_VALUE
9062     InitField(newx, newy, FALSE);
9063 #endif
9064   }
9065   else if (element == EL_DC_MAGIC_WALL_FILLING)
9066   {
9067     element = Feld[newx][newy] = get_next_element(element);
9068     if (!game.magic_wall_active)
9069       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9070     Store[newx][newy] = Store[x][y];
9071   }
9072   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9073   {
9074     Feld[x][y] = get_next_element(element);
9075     if (!game.magic_wall_active)
9076       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9077     element = Feld[newx][newy] = Store[x][y];
9078
9079 #if USE_NEW_CUSTOM_VALUE
9080     InitField(newx, newy, FALSE);
9081 #endif
9082   }
9083   else if (element == EL_AMOEBA_DROPPING)
9084   {
9085     Feld[x][y] = get_next_element(element);
9086     element = Feld[newx][newy] = Store[x][y];
9087   }
9088   else if (element == EL_SOKOBAN_OBJECT)
9089   {
9090     if (Back[x][y])
9091       Feld[x][y] = Back[x][y];
9092
9093     if (Back[newx][newy])
9094       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9095
9096     Back[x][y] = Back[newx][newy] = 0;
9097   }
9098
9099   Store[x][y] = EL_EMPTY;
9100   MovPos[x][y] = 0;
9101   MovDir[x][y] = 0;
9102   MovDelay[x][y] = 0;
9103
9104   MovDelay[newx][newy] = 0;
9105
9106   if (CAN_CHANGE_OR_HAS_ACTION(element))
9107   {
9108     /* copy element change control values to new field */
9109     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9110     ChangePage[newx][newy]  = ChangePage[x][y];
9111     ChangeCount[newx][newy] = ChangeCount[x][y];
9112     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9113   }
9114
9115 #if USE_NEW_CUSTOM_VALUE
9116   CustomValue[newx][newy] = CustomValue[x][y];
9117 #endif
9118
9119   ChangeDelay[x][y] = 0;
9120   ChangePage[x][y] = -1;
9121   ChangeCount[x][y] = 0;
9122   ChangeEvent[x][y] = -1;
9123
9124 #if USE_NEW_CUSTOM_VALUE
9125   CustomValue[x][y] = 0;
9126 #endif
9127
9128   /* copy animation control values to new field */
9129   GfxFrame[newx][newy]  = GfxFrame[x][y];
9130   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9131   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9132   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9133
9134   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9135
9136   /* some elements can leave other elements behind after moving */
9137 #if 1
9138   if (ei->move_leave_element != EL_EMPTY &&
9139       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9140       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9141 #else
9142   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9143       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9144       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9145 #endif
9146   {
9147     int move_leave_element = ei->move_leave_element;
9148
9149 #if 1
9150 #if 1
9151     /* this makes it possible to leave the removed element again */
9152     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9153       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9154 #else
9155     /* this makes it possible to leave the removed element again */
9156     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9157       move_leave_element = stored;
9158 #endif
9159 #else
9160     /* this makes it possible to leave the removed element again */
9161     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9162         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9163       move_leave_element = stored;
9164 #endif
9165
9166     Feld[x][y] = move_leave_element;
9167
9168     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9169       MovDir[x][y] = direction;
9170
9171     InitField(x, y, FALSE);
9172
9173     if (GFX_CRUMBLED(Feld[x][y]))
9174       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9175
9176     if (ELEM_IS_PLAYER(move_leave_element))
9177       RelocatePlayer(x, y, move_leave_element);
9178   }
9179
9180   /* do this after checking for left-behind element */
9181   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9182
9183   if (!CAN_MOVE(element) ||
9184       (CAN_FALL(element) && direction == MV_DOWN &&
9185        (element == EL_SPRING ||
9186         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9187         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9188     GfxDir[x][y] = MovDir[newx][newy] = 0;
9189
9190   TEST_DrawLevelField(x, y);
9191   TEST_DrawLevelField(newx, newy);
9192
9193   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9194
9195   /* prevent pushed element from moving on in pushed direction */
9196   if (pushed_by_player && CAN_MOVE(element) &&
9197       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9198       !(element_info[element].move_pattern & direction))
9199     TurnRound(newx, newy);
9200
9201   /* prevent elements on conveyor belt from moving on in last direction */
9202   if (pushed_by_conveyor && CAN_FALL(element) &&
9203       direction & MV_HORIZONTAL)
9204     MovDir[newx][newy] = 0;
9205
9206   if (!pushed_by_player)
9207   {
9208     int nextx = newx + dx, nexty = newy + dy;
9209     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9210
9211     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9212
9213     if (CAN_FALL(element) && direction == MV_DOWN)
9214       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9215
9216     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9217       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9218
9219 #if USE_FIX_IMPACT_COLLISION
9220     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9221       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9222 #endif
9223   }
9224
9225   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9226   {
9227     TestIfBadThingTouchesPlayer(newx, newy);
9228     TestIfBadThingTouchesFriend(newx, newy);
9229
9230     if (!IS_CUSTOM_ELEMENT(element))
9231       TestIfBadThingTouchesOtherBadThing(newx, newy);
9232   }
9233   else if (element == EL_PENGUIN)
9234     TestIfFriendTouchesBadThing(newx, newy);
9235
9236   if (DONT_GET_HIT_BY(element))
9237   {
9238     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9239   }
9240
9241   /* give the player one last chance (one more frame) to move away */
9242   if (CAN_FALL(element) && direction == MV_DOWN &&
9243       (last_line || (!IS_FREE(x, newy + 1) &&
9244                      (!IS_PLAYER(x, newy + 1) ||
9245                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9246     Impact(x, newy);
9247
9248   if (pushed_by_player && !game.use_change_when_pushing_bug)
9249   {
9250     int push_side = MV_DIR_OPPOSITE(direction);
9251     struct PlayerInfo *player = PLAYERINFO(x, y);
9252
9253     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9254                                player->index_bit, push_side);
9255     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9256                                         player->index_bit, push_side);
9257   }
9258
9259   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9260     MovDelay[newx][newy] = 1;
9261
9262   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9263
9264   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9265
9266 #if 0
9267   if (ChangePage[newx][newy] != -1)             /* delayed change */
9268   {
9269     int page = ChangePage[newx][newy];
9270     struct ElementChangeInfo *change = &ei->change_page[page];
9271
9272     ChangePage[newx][newy] = -1;
9273
9274     if (change->can_change)
9275     {
9276       if (ChangeElement(newx, newy, element, page))
9277       {
9278         if (change->post_change_function)
9279           change->post_change_function(newx, newy);
9280       }
9281     }
9282
9283     if (change->has_action)
9284       ExecuteCustomElementAction(newx, newy, element, page);
9285   }
9286 #endif
9287
9288   TestIfElementHitsCustomElement(newx, newy, direction);
9289   TestIfPlayerTouchesCustomElement(newx, newy);
9290   TestIfElementTouchesCustomElement(newx, newy);
9291
9292   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9293       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9294     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9295                              MV_DIR_OPPOSITE(direction));
9296 }
9297
9298 int AmoebeNachbarNr(int ax, int ay)
9299 {
9300   int i;
9301   int element = Feld[ax][ay];
9302   int group_nr = 0;
9303   static int xy[4][2] =
9304   {
9305     { 0, -1 },
9306     { -1, 0 },
9307     { +1, 0 },
9308     { 0, +1 }
9309   };
9310
9311   for (i = 0; i < NUM_DIRECTIONS; i++)
9312   {
9313     int x = ax + xy[i][0];
9314     int y = ay + xy[i][1];
9315
9316     if (!IN_LEV_FIELD(x, y))
9317       continue;
9318
9319     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9320       group_nr = AmoebaNr[x][y];
9321   }
9322
9323   return group_nr;
9324 }
9325
9326 void AmoebenVereinigen(int ax, int ay)
9327 {
9328   int i, x, y, xx, yy;
9329   int new_group_nr = AmoebaNr[ax][ay];
9330   static int xy[4][2] =
9331   {
9332     { 0, -1 },
9333     { -1, 0 },
9334     { +1, 0 },
9335     { 0, +1 }
9336   };
9337
9338   if (new_group_nr == 0)
9339     return;
9340
9341   for (i = 0; i < NUM_DIRECTIONS; i++)
9342   {
9343     x = ax + xy[i][0];
9344     y = ay + xy[i][1];
9345
9346     if (!IN_LEV_FIELD(x, y))
9347       continue;
9348
9349     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9350          Feld[x][y] == EL_BD_AMOEBA ||
9351          Feld[x][y] == EL_AMOEBA_DEAD) &&
9352         AmoebaNr[x][y] != new_group_nr)
9353     {
9354       int old_group_nr = AmoebaNr[x][y];
9355
9356       if (old_group_nr == 0)
9357         return;
9358
9359       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9360       AmoebaCnt[old_group_nr] = 0;
9361       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9362       AmoebaCnt2[old_group_nr] = 0;
9363
9364       SCAN_PLAYFIELD(xx, yy)
9365       {
9366         if (AmoebaNr[xx][yy] == old_group_nr)
9367           AmoebaNr[xx][yy] = new_group_nr;
9368       }
9369     }
9370   }
9371 }
9372
9373 void AmoebeUmwandeln(int ax, int ay)
9374 {
9375   int i, x, y;
9376
9377   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9378   {
9379     int group_nr = AmoebaNr[ax][ay];
9380
9381 #ifdef DEBUG
9382     if (group_nr == 0)
9383     {
9384       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9385       printf("AmoebeUmwandeln(): This should never happen!\n");
9386       return;
9387     }
9388 #endif
9389
9390     SCAN_PLAYFIELD(x, y)
9391     {
9392       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9393       {
9394         AmoebaNr[x][y] = 0;
9395         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9396       }
9397     }
9398
9399     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9400                             SND_AMOEBA_TURNING_TO_GEM :
9401                             SND_AMOEBA_TURNING_TO_ROCK));
9402     Bang(ax, ay);
9403   }
9404   else
9405   {
9406     static int xy[4][2] =
9407     {
9408       { 0, -1 },
9409       { -1, 0 },
9410       { +1, 0 },
9411       { 0, +1 }
9412     };
9413
9414     for (i = 0; i < NUM_DIRECTIONS; i++)
9415     {
9416       x = ax + xy[i][0];
9417       y = ay + xy[i][1];
9418
9419       if (!IN_LEV_FIELD(x, y))
9420         continue;
9421
9422       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9423       {
9424         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9425                               SND_AMOEBA_TURNING_TO_GEM :
9426                               SND_AMOEBA_TURNING_TO_ROCK));
9427         Bang(x, y);
9428       }
9429     }
9430   }
9431 }
9432
9433 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9434 {
9435   int x, y;
9436   int group_nr = AmoebaNr[ax][ay];
9437   boolean done = FALSE;
9438
9439 #ifdef DEBUG
9440   if (group_nr == 0)
9441   {
9442     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9443     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9444     return;
9445   }
9446 #endif
9447
9448   SCAN_PLAYFIELD(x, y)
9449   {
9450     if (AmoebaNr[x][y] == group_nr &&
9451         (Feld[x][y] == EL_AMOEBA_DEAD ||
9452          Feld[x][y] == EL_BD_AMOEBA ||
9453          Feld[x][y] == EL_AMOEBA_GROWING))
9454     {
9455       AmoebaNr[x][y] = 0;
9456       Feld[x][y] = new_element;
9457       InitField(x, y, FALSE);
9458       TEST_DrawLevelField(x, y);
9459       done = TRUE;
9460     }
9461   }
9462
9463   if (done)
9464     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9465                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9466                             SND_BD_AMOEBA_TURNING_TO_GEM));
9467 }
9468
9469 void AmoebeWaechst(int x, int y)
9470 {
9471   static unsigned long sound_delay = 0;
9472   static unsigned long sound_delay_value = 0;
9473
9474   if (!MovDelay[x][y])          /* start new growing cycle */
9475   {
9476     MovDelay[x][y] = 7;
9477
9478     if (DelayReached(&sound_delay, sound_delay_value))
9479     {
9480       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9481       sound_delay_value = 30;
9482     }
9483   }
9484
9485   if (MovDelay[x][y])           /* wait some time before growing bigger */
9486   {
9487     MovDelay[x][y]--;
9488     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9489     {
9490       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9491                                            6 - MovDelay[x][y]);
9492
9493       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9494     }
9495
9496     if (!MovDelay[x][y])
9497     {
9498       Feld[x][y] = Store[x][y];
9499       Store[x][y] = 0;
9500       TEST_DrawLevelField(x, y);
9501     }
9502   }
9503 }
9504
9505 void AmoebaDisappearing(int x, int y)
9506 {
9507   static unsigned long sound_delay = 0;
9508   static unsigned long sound_delay_value = 0;
9509
9510   if (!MovDelay[x][y])          /* start new shrinking cycle */
9511   {
9512     MovDelay[x][y] = 7;
9513
9514     if (DelayReached(&sound_delay, sound_delay_value))
9515       sound_delay_value = 30;
9516   }
9517
9518   if (MovDelay[x][y])           /* wait some time before shrinking */
9519   {
9520     MovDelay[x][y]--;
9521     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9522     {
9523       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9524                                            6 - MovDelay[x][y]);
9525
9526       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9527     }
9528
9529     if (!MovDelay[x][y])
9530     {
9531       Feld[x][y] = EL_EMPTY;
9532       TEST_DrawLevelField(x, y);
9533
9534       /* don't let mole enter this field in this cycle;
9535          (give priority to objects falling to this field from above) */
9536       Stop[x][y] = TRUE;
9537     }
9538   }
9539 }
9540
9541 void AmoebeAbleger(int ax, int ay)
9542 {
9543   int i;
9544   int element = Feld[ax][ay];
9545   int graphic = el2img(element);
9546   int newax = ax, neway = ay;
9547   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9548   static int xy[4][2] =
9549   {
9550     { 0, -1 },
9551     { -1, 0 },
9552     { +1, 0 },
9553     { 0, +1 }
9554   };
9555
9556   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9557   {
9558     Feld[ax][ay] = EL_AMOEBA_DEAD;
9559     TEST_DrawLevelField(ax, ay);
9560     return;
9561   }
9562
9563   if (IS_ANIMATED(graphic))
9564     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9565
9566   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9567     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9568
9569   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9570   {
9571     MovDelay[ax][ay]--;
9572     if (MovDelay[ax][ay])
9573       return;
9574   }
9575
9576   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9577   {
9578     int start = RND(4);
9579     int x = ax + xy[start][0];
9580     int y = ay + xy[start][1];
9581
9582     if (!IN_LEV_FIELD(x, y))
9583       return;
9584
9585     if (IS_FREE(x, y) ||
9586         CAN_GROW_INTO(Feld[x][y]) ||
9587         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9588         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9589     {
9590       newax = x;
9591       neway = y;
9592     }
9593
9594     if (newax == ax && neway == ay)
9595       return;
9596   }
9597   else                          /* normal or "filled" (BD style) amoeba */
9598   {
9599     int start = RND(4);
9600     boolean waiting_for_player = FALSE;
9601
9602     for (i = 0; i < NUM_DIRECTIONS; i++)
9603     {
9604       int j = (start + i) % 4;
9605       int x = ax + xy[j][0];
9606       int y = ay + xy[j][1];
9607
9608       if (!IN_LEV_FIELD(x, y))
9609         continue;
9610
9611       if (IS_FREE(x, y) ||
9612           CAN_GROW_INTO(Feld[x][y]) ||
9613           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9614           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9615       {
9616         newax = x;
9617         neway = y;
9618         break;
9619       }
9620       else if (IS_PLAYER(x, y))
9621         waiting_for_player = TRUE;
9622     }
9623
9624     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9625     {
9626       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9627       {
9628         Feld[ax][ay] = EL_AMOEBA_DEAD;
9629         TEST_DrawLevelField(ax, ay);
9630         AmoebaCnt[AmoebaNr[ax][ay]]--;
9631
9632         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9633         {
9634           if (element == EL_AMOEBA_FULL)
9635             AmoebeUmwandeln(ax, ay);
9636           else if (element == EL_BD_AMOEBA)
9637             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9638         }
9639       }
9640       return;
9641     }
9642     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9643     {
9644       /* amoeba gets larger by growing in some direction */
9645
9646       int new_group_nr = AmoebaNr[ax][ay];
9647
9648 #ifdef DEBUG
9649   if (new_group_nr == 0)
9650   {
9651     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9652     printf("AmoebeAbleger(): This should never happen!\n");
9653     return;
9654   }
9655 #endif
9656
9657       AmoebaNr[newax][neway] = new_group_nr;
9658       AmoebaCnt[new_group_nr]++;
9659       AmoebaCnt2[new_group_nr]++;
9660
9661       /* if amoeba touches other amoeba(s) after growing, unify them */
9662       AmoebenVereinigen(newax, neway);
9663
9664       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9665       {
9666         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9667         return;
9668       }
9669     }
9670   }
9671
9672   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9673       (neway == lev_fieldy - 1 && newax != ax))
9674   {
9675     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9676     Store[newax][neway] = element;
9677   }
9678   else if (neway == ay || element == EL_EMC_DRIPPER)
9679   {
9680     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9681
9682     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9683   }
9684   else
9685   {
9686     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9687     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9688     Store[ax][ay] = EL_AMOEBA_DROP;
9689     ContinueMoving(ax, ay);
9690     return;
9691   }
9692
9693   TEST_DrawLevelField(newax, neway);
9694 }
9695
9696 void Life(int ax, int ay)
9697 {
9698   int x1, y1, x2, y2;
9699   int life_time = 40;
9700   int element = Feld[ax][ay];
9701   int graphic = el2img(element);
9702   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9703                          level.biomaze);
9704   boolean changed = FALSE;
9705
9706   if (IS_ANIMATED(graphic))
9707     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9708
9709   if (Stop[ax][ay])
9710     return;
9711
9712   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9713     MovDelay[ax][ay] = life_time;
9714
9715   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9716   {
9717     MovDelay[ax][ay]--;
9718     if (MovDelay[ax][ay])
9719       return;
9720   }
9721
9722   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9723   {
9724     int xx = ax+x1, yy = ay+y1;
9725     int nachbarn = 0;
9726
9727     if (!IN_LEV_FIELD(xx, yy))
9728       continue;
9729
9730     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9731     {
9732       int x = xx+x2, y = yy+y2;
9733
9734       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9735         continue;
9736
9737       if (((Feld[x][y] == element ||
9738             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9739            !Stop[x][y]) ||
9740           (IS_FREE(x, y) && Stop[x][y]))
9741         nachbarn++;
9742     }
9743
9744     if (xx == ax && yy == ay)           /* field in the middle */
9745     {
9746       if (nachbarn < life_parameter[0] ||
9747           nachbarn > life_parameter[1])
9748       {
9749         Feld[xx][yy] = EL_EMPTY;
9750         if (!Stop[xx][yy])
9751           TEST_DrawLevelField(xx, yy);
9752         Stop[xx][yy] = TRUE;
9753         changed = TRUE;
9754       }
9755     }
9756     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9757     {                                   /* free border field */
9758       if (nachbarn >= life_parameter[2] &&
9759           nachbarn <= life_parameter[3])
9760       {
9761         Feld[xx][yy] = element;
9762         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9763         if (!Stop[xx][yy])
9764           TEST_DrawLevelField(xx, yy);
9765         Stop[xx][yy] = TRUE;
9766         changed = TRUE;
9767       }
9768     }
9769   }
9770
9771   if (changed)
9772     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9773                    SND_GAME_OF_LIFE_GROWING);
9774 }
9775
9776 static void InitRobotWheel(int x, int y)
9777 {
9778   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9779 }
9780
9781 static void RunRobotWheel(int x, int y)
9782 {
9783   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9784 }
9785
9786 static void StopRobotWheel(int x, int y)
9787 {
9788   if (ZX == x && ZY == y)
9789   {
9790     ZX = ZY = -1;
9791
9792     game.robot_wheel_active = FALSE;
9793   }
9794 }
9795
9796 static void InitTimegateWheel(int x, int y)
9797 {
9798   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9799 }
9800
9801 static void RunTimegateWheel(int x, int y)
9802 {
9803   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9804 }
9805
9806 static void InitMagicBallDelay(int x, int y)
9807 {
9808 #if 1
9809   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9810 #else
9811   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9812 #endif
9813 }
9814
9815 static void ActivateMagicBall(int bx, int by)
9816 {
9817   int x, y;
9818
9819   if (level.ball_random)
9820   {
9821     int pos_border = RND(8);    /* select one of the eight border elements */
9822     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9823     int xx = pos_content % 3;
9824     int yy = pos_content / 3;
9825
9826     x = bx - 1 + xx;
9827     y = by - 1 + yy;
9828
9829     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9830       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9831   }
9832   else
9833   {
9834     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9835     {
9836       int xx = x - bx + 1;
9837       int yy = y - by + 1;
9838
9839       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9840         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9841     }
9842   }
9843
9844   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9845 }
9846
9847 void CheckExit(int x, int y)
9848 {
9849   if (local_player->gems_still_needed > 0 ||
9850       local_player->sokobanfields_still_needed > 0 ||
9851       local_player->lights_still_needed > 0)
9852   {
9853     int element = Feld[x][y];
9854     int graphic = el2img(element);
9855
9856     if (IS_ANIMATED(graphic))
9857       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9858
9859     return;
9860   }
9861
9862   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9863     return;
9864
9865   Feld[x][y] = EL_EXIT_OPENING;
9866
9867   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9868 }
9869
9870 void CheckExitEM(int x, int y)
9871 {
9872   if (local_player->gems_still_needed > 0 ||
9873       local_player->sokobanfields_still_needed > 0 ||
9874       local_player->lights_still_needed > 0)
9875   {
9876     int element = Feld[x][y];
9877     int graphic = el2img(element);
9878
9879     if (IS_ANIMATED(graphic))
9880       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9881
9882     return;
9883   }
9884
9885   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9886     return;
9887
9888   Feld[x][y] = EL_EM_EXIT_OPENING;
9889
9890   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9891 }
9892
9893 void CheckExitSteel(int x, int y)
9894 {
9895   if (local_player->gems_still_needed > 0 ||
9896       local_player->sokobanfields_still_needed > 0 ||
9897       local_player->lights_still_needed > 0)
9898   {
9899     int element = Feld[x][y];
9900     int graphic = el2img(element);
9901
9902     if (IS_ANIMATED(graphic))
9903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9904
9905     return;
9906   }
9907
9908   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9909     return;
9910
9911   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9912
9913   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9914 }
9915
9916 void CheckExitSteelEM(int x, int y)
9917 {
9918   if (local_player->gems_still_needed > 0 ||
9919       local_player->sokobanfields_still_needed > 0 ||
9920       local_player->lights_still_needed > 0)
9921   {
9922     int element = Feld[x][y];
9923     int graphic = el2img(element);
9924
9925     if (IS_ANIMATED(graphic))
9926       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9927
9928     return;
9929   }
9930
9931   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9932     return;
9933
9934   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9935
9936   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9937 }
9938
9939 void CheckExitSP(int x, int y)
9940 {
9941   if (local_player->gems_still_needed > 0)
9942   {
9943     int element = Feld[x][y];
9944     int graphic = el2img(element);
9945
9946     if (IS_ANIMATED(graphic))
9947       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9948
9949     return;
9950   }
9951
9952   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9953     return;
9954
9955   Feld[x][y] = EL_SP_EXIT_OPENING;
9956
9957   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9958 }
9959
9960 static void CloseAllOpenTimegates()
9961 {
9962   int x, y;
9963
9964   SCAN_PLAYFIELD(x, y)
9965   {
9966     int element = Feld[x][y];
9967
9968     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9969     {
9970       Feld[x][y] = EL_TIMEGATE_CLOSING;
9971
9972       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9973     }
9974   }
9975 }
9976
9977 void DrawTwinkleOnField(int x, int y)
9978 {
9979   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9980     return;
9981
9982   if (Feld[x][y] == EL_BD_DIAMOND)
9983     return;
9984
9985   if (MovDelay[x][y] == 0)      /* next animation frame */
9986     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9987
9988   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9989   {
9990     MovDelay[x][y]--;
9991
9992     DrawLevelElementAnimation(x, y, Feld[x][y]);
9993
9994     if (MovDelay[x][y] != 0)
9995     {
9996       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9997                                            10 - MovDelay[x][y]);
9998
9999       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10000     }
10001   }
10002 }
10003
10004 void MauerWaechst(int x, int y)
10005 {
10006   int delay = 6;
10007
10008   if (!MovDelay[x][y])          /* next animation frame */
10009     MovDelay[x][y] = 3 * delay;
10010
10011   if (MovDelay[x][y])           /* wait some time before next frame */
10012   {
10013     MovDelay[x][y]--;
10014
10015     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10016     {
10017       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10018       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10019
10020       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10021     }
10022
10023     if (!MovDelay[x][y])
10024     {
10025       if (MovDir[x][y] == MV_LEFT)
10026       {
10027         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10028           TEST_DrawLevelField(x - 1, y);
10029       }
10030       else if (MovDir[x][y] == MV_RIGHT)
10031       {
10032         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10033           TEST_DrawLevelField(x + 1, y);
10034       }
10035       else if (MovDir[x][y] == MV_UP)
10036       {
10037         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10038           TEST_DrawLevelField(x, y - 1);
10039       }
10040       else
10041       {
10042         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10043           TEST_DrawLevelField(x, y + 1);
10044       }
10045
10046       Feld[x][y] = Store[x][y];
10047       Store[x][y] = 0;
10048       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10049       TEST_DrawLevelField(x, y);
10050     }
10051   }
10052 }
10053
10054 void MauerAbleger(int ax, int ay)
10055 {
10056   int element = Feld[ax][ay];
10057   int graphic = el2img(element);
10058   boolean oben_frei = FALSE, unten_frei = FALSE;
10059   boolean links_frei = FALSE, rechts_frei = FALSE;
10060   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10061   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10062   boolean new_wall = FALSE;
10063
10064   if (IS_ANIMATED(graphic))
10065     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10066
10067   if (!MovDelay[ax][ay])        /* start building new wall */
10068     MovDelay[ax][ay] = 6;
10069
10070   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10071   {
10072     MovDelay[ax][ay]--;
10073     if (MovDelay[ax][ay])
10074       return;
10075   }
10076
10077   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10078     oben_frei = TRUE;
10079   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10080     unten_frei = TRUE;
10081   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10082     links_frei = TRUE;
10083   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10084     rechts_frei = TRUE;
10085
10086   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10087       element == EL_EXPANDABLE_WALL_ANY)
10088   {
10089     if (oben_frei)
10090     {
10091       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10092       Store[ax][ay-1] = element;
10093       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10094       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10095         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10096                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10097       new_wall = TRUE;
10098     }
10099     if (unten_frei)
10100     {
10101       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10102       Store[ax][ay+1] = element;
10103       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10104       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10105         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10106                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10107       new_wall = TRUE;
10108     }
10109   }
10110
10111   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10112       element == EL_EXPANDABLE_WALL_ANY ||
10113       element == EL_EXPANDABLE_WALL ||
10114       element == EL_BD_EXPANDABLE_WALL)
10115   {
10116     if (links_frei)
10117     {
10118       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10119       Store[ax-1][ay] = element;
10120       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10121       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10122         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10123                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10124       new_wall = TRUE;
10125     }
10126
10127     if (rechts_frei)
10128     {
10129       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10130       Store[ax+1][ay] = element;
10131       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10132       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10133         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10134                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10135       new_wall = TRUE;
10136     }
10137   }
10138
10139   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10140     TEST_DrawLevelField(ax, ay);
10141
10142   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10143     oben_massiv = TRUE;
10144   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10145     unten_massiv = TRUE;
10146   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10147     links_massiv = TRUE;
10148   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10149     rechts_massiv = TRUE;
10150
10151   if (((oben_massiv && unten_massiv) ||
10152        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10153        element == EL_EXPANDABLE_WALL) &&
10154       ((links_massiv && rechts_massiv) ||
10155        element == EL_EXPANDABLE_WALL_VERTICAL))
10156     Feld[ax][ay] = EL_WALL;
10157
10158   if (new_wall)
10159     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10160 }
10161
10162 void MauerAblegerStahl(int ax, int ay)
10163 {
10164   int element = Feld[ax][ay];
10165   int graphic = el2img(element);
10166   boolean oben_frei = FALSE, unten_frei = FALSE;
10167   boolean links_frei = FALSE, rechts_frei = FALSE;
10168   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10169   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10170   boolean new_wall = FALSE;
10171
10172   if (IS_ANIMATED(graphic))
10173     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10174
10175   if (!MovDelay[ax][ay])        /* start building new wall */
10176     MovDelay[ax][ay] = 6;
10177
10178   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10179   {
10180     MovDelay[ax][ay]--;
10181     if (MovDelay[ax][ay])
10182       return;
10183   }
10184
10185   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10186     oben_frei = TRUE;
10187   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10188     unten_frei = TRUE;
10189   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10190     links_frei = TRUE;
10191   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10192     rechts_frei = TRUE;
10193
10194   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10195       element == EL_EXPANDABLE_STEELWALL_ANY)
10196   {
10197     if (oben_frei)
10198     {
10199       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10200       Store[ax][ay-1] = element;
10201       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10202       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10203         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10204                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10205       new_wall = TRUE;
10206     }
10207     if (unten_frei)
10208     {
10209       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10210       Store[ax][ay+1] = element;
10211       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10212       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10213         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10214                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10215       new_wall = TRUE;
10216     }
10217   }
10218
10219   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10220       element == EL_EXPANDABLE_STEELWALL_ANY)
10221   {
10222     if (links_frei)
10223     {
10224       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10225       Store[ax-1][ay] = element;
10226       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10227       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10228         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10229                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10230       new_wall = TRUE;
10231     }
10232
10233     if (rechts_frei)
10234     {
10235       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10236       Store[ax+1][ay] = element;
10237       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10238       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10239         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10240                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10241       new_wall = TRUE;
10242     }
10243   }
10244
10245   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10246     oben_massiv = TRUE;
10247   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10248     unten_massiv = TRUE;
10249   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10250     links_massiv = TRUE;
10251   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10252     rechts_massiv = TRUE;
10253
10254   if (((oben_massiv && unten_massiv) ||
10255        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10256       ((links_massiv && rechts_massiv) ||
10257        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10258     Feld[ax][ay] = EL_STEELWALL;
10259
10260   if (new_wall)
10261     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10262 }
10263
10264 void CheckForDragon(int x, int y)
10265 {
10266   int i, j;
10267   boolean dragon_found = FALSE;
10268   static int xy[4][2] =
10269   {
10270     { 0, -1 },
10271     { -1, 0 },
10272     { +1, 0 },
10273     { 0, +1 }
10274   };
10275
10276   for (i = 0; i < NUM_DIRECTIONS; i++)
10277   {
10278     for (j = 0; j < 4; j++)
10279     {
10280       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10281
10282       if (IN_LEV_FIELD(xx, yy) &&
10283           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10284       {
10285         if (Feld[xx][yy] == EL_DRAGON)
10286           dragon_found = TRUE;
10287       }
10288       else
10289         break;
10290     }
10291   }
10292
10293   if (!dragon_found)
10294   {
10295     for (i = 0; i < NUM_DIRECTIONS; i++)
10296     {
10297       for (j = 0; j < 3; j++)
10298       {
10299         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10300   
10301         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10302         {
10303           Feld[xx][yy] = EL_EMPTY;
10304           TEST_DrawLevelField(xx, yy);
10305         }
10306         else
10307           break;
10308       }
10309     }
10310   }
10311 }
10312
10313 static void InitBuggyBase(int x, int y)
10314 {
10315   int element = Feld[x][y];
10316   int activating_delay = FRAMES_PER_SECOND / 4;
10317
10318   ChangeDelay[x][y] =
10319     (element == EL_SP_BUGGY_BASE ?
10320      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10321      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10322      activating_delay :
10323      element == EL_SP_BUGGY_BASE_ACTIVE ?
10324      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10325 }
10326
10327 static void WarnBuggyBase(int x, int y)
10328 {
10329   int i;
10330   static int xy[4][2] =
10331   {
10332     { 0, -1 },
10333     { -1, 0 },
10334     { +1, 0 },
10335     { 0, +1 }
10336   };
10337
10338   for (i = 0; i < NUM_DIRECTIONS; i++)
10339   {
10340     int xx = x + xy[i][0];
10341     int yy = y + xy[i][1];
10342
10343     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10344     {
10345       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10346
10347       break;
10348     }
10349   }
10350 }
10351
10352 static void InitTrap(int x, int y)
10353 {
10354   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10355 }
10356
10357 static void ActivateTrap(int x, int y)
10358 {
10359   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10360 }
10361
10362 static void ChangeActiveTrap(int x, int y)
10363 {
10364   int graphic = IMG_TRAP_ACTIVE;
10365
10366   /* if new animation frame was drawn, correct crumbled sand border */
10367   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10368     TEST_DrawLevelFieldCrumbledSand(x, y);
10369 }
10370
10371 static int getSpecialActionElement(int element, int number, int base_element)
10372 {
10373   return (element != EL_EMPTY ? element :
10374           number != -1 ? base_element + number - 1 :
10375           EL_EMPTY);
10376 }
10377
10378 static int getModifiedActionNumber(int value_old, int operator, int operand,
10379                                    int value_min, int value_max)
10380 {
10381   int value_new = (operator == CA_MODE_SET      ? operand :
10382                    operator == CA_MODE_ADD      ? value_old + operand :
10383                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10384                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10385                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10386                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10387                    value_old);
10388
10389   return (value_new < value_min ? value_min :
10390           value_new > value_max ? value_max :
10391           value_new);
10392 }
10393
10394 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10395 {
10396   struct ElementInfo *ei = &element_info[element];
10397   struct ElementChangeInfo *change = &ei->change_page[page];
10398   int target_element = change->target_element;
10399   int action_type = change->action_type;
10400   int action_mode = change->action_mode;
10401   int action_arg = change->action_arg;
10402   int action_element = change->action_element;
10403   int i;
10404
10405   if (!change->has_action)
10406     return;
10407
10408   /* ---------- determine action paramater values -------------------------- */
10409
10410   int level_time_value =
10411     (level.time > 0 ? TimeLeft :
10412      TimePlayed);
10413
10414   int action_arg_element_raw =
10415     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10416      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10417      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10418      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10419      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10420      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10421      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10422      EL_EMPTY);
10423   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10424
10425 #if 0
10426   if (action_arg_element_raw == EL_GROUP_START)
10427     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10428 #endif
10429
10430   int action_arg_direction =
10431     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10432      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10433      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10434      change->actual_trigger_side :
10435      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10436      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10437      MV_NONE);
10438
10439   int action_arg_number_min =
10440     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10441      CA_ARG_MIN);
10442
10443   int action_arg_number_max =
10444     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10445      action_type == CA_SET_LEVEL_GEMS ? 999 :
10446      action_type == CA_SET_LEVEL_TIME ? 9999 :
10447      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10448      action_type == CA_SET_CE_VALUE ? 9999 :
10449      action_type == CA_SET_CE_SCORE ? 9999 :
10450      CA_ARG_MAX);
10451
10452   int action_arg_number_reset =
10453     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10454      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10455      action_type == CA_SET_LEVEL_TIME ? level.time :
10456      action_type == CA_SET_LEVEL_SCORE ? 0 :
10457 #if USE_NEW_CUSTOM_VALUE
10458      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10459 #else
10460      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10461 #endif
10462      action_type == CA_SET_CE_SCORE ? 0 :
10463      0);
10464
10465   int action_arg_number =
10466     (action_arg <= CA_ARG_MAX ? action_arg :
10467      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10468      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10469      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10470      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10471      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10472      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10473 #if USE_NEW_CUSTOM_VALUE
10474      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10475 #else
10476      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10477 #endif
10478      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10479      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10480      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10481      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10482      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10483      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10484      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10485      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10486      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10487      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10488      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10489      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10490      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10491      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10492      -1);
10493
10494   int action_arg_number_old =
10495     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10496      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10497      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10498      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10499      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10500      0);
10501
10502   int action_arg_number_new =
10503     getModifiedActionNumber(action_arg_number_old,
10504                             action_mode, action_arg_number,
10505                             action_arg_number_min, action_arg_number_max);
10506
10507 #if 1
10508   int trigger_player_bits =
10509     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10510      change->actual_trigger_player_bits : change->trigger_player);
10511 #else
10512   int trigger_player_bits =
10513     (change->actual_trigger_player >= EL_PLAYER_1 &&
10514      change->actual_trigger_player <= EL_PLAYER_4 ?
10515      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10516      PLAYER_BITS_ANY);
10517 #endif
10518
10519   int action_arg_player_bits =
10520     (action_arg >= CA_ARG_PLAYER_1 &&
10521      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10522      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10523      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10524      PLAYER_BITS_ANY);
10525
10526   /* ---------- execute action  -------------------------------------------- */
10527
10528   switch (action_type)
10529   {
10530     case CA_NO_ACTION:
10531     {
10532       return;
10533     }
10534
10535     /* ---------- level actions  ------------------------------------------- */
10536
10537     case CA_RESTART_LEVEL:
10538     {
10539       game.restart_level = TRUE;
10540
10541       break;
10542     }
10543
10544     case CA_SHOW_ENVELOPE:
10545     {
10546       int element = getSpecialActionElement(action_arg_element,
10547                                             action_arg_number, EL_ENVELOPE_1);
10548
10549       if (IS_ENVELOPE(element))
10550         local_player->show_envelope = element;
10551
10552       break;
10553     }
10554
10555     case CA_SET_LEVEL_TIME:
10556     {
10557       if (level.time > 0)       /* only modify limited time value */
10558       {
10559         TimeLeft = action_arg_number_new;
10560
10561 #if 1
10562         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10563
10564         DisplayGameControlValues();
10565 #else
10566         DrawGameValue_Time(TimeLeft);
10567 #endif
10568
10569         if (!TimeLeft && setup.time_limit)
10570           for (i = 0; i < MAX_PLAYERS; i++)
10571             KillPlayer(&stored_player[i]);
10572       }
10573
10574       break;
10575     }
10576
10577     case CA_SET_LEVEL_SCORE:
10578     {
10579       local_player->score = action_arg_number_new;
10580
10581 #if 1
10582       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10583
10584       DisplayGameControlValues();
10585 #else
10586       DrawGameValue_Score(local_player->score);
10587 #endif
10588
10589       break;
10590     }
10591
10592     case CA_SET_LEVEL_GEMS:
10593     {
10594       local_player->gems_still_needed = action_arg_number_new;
10595
10596 #if 1
10597       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10598
10599       DisplayGameControlValues();
10600 #else
10601       DrawGameValue_Emeralds(local_player->gems_still_needed);
10602 #endif
10603
10604       break;
10605     }
10606
10607 #if !USE_PLAYER_GRAVITY
10608     case CA_SET_LEVEL_GRAVITY:
10609     {
10610       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10611                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10612                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10613                       game.gravity);
10614       break;
10615     }
10616 #endif
10617
10618     case CA_SET_LEVEL_WIND:
10619     {
10620       game.wind_direction = action_arg_direction;
10621
10622       break;
10623     }
10624
10625     case CA_SET_LEVEL_RANDOM_SEED:
10626     {
10627 #if 1
10628       /* ensure that setting a new random seed while playing is predictable */
10629       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10630 #else
10631       InitRND(action_arg_number_new);
10632 #endif
10633
10634 #if 0
10635       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10636 #endif
10637
10638 #if 0
10639       {
10640         int i;
10641
10642         printf("::: ");
10643         for (i = 0; i < 9; i++)
10644           printf("%d, ", RND(2));
10645         printf("\n");
10646       }
10647 #endif
10648
10649       break;
10650     }
10651
10652     /* ---------- player actions  ------------------------------------------ */
10653
10654     case CA_MOVE_PLAYER:
10655     {
10656       /* automatically move to the next field in specified direction */
10657       for (i = 0; i < MAX_PLAYERS; i++)
10658         if (trigger_player_bits & (1 << i))
10659           stored_player[i].programmed_action = action_arg_direction;
10660
10661       break;
10662     }
10663
10664     case CA_EXIT_PLAYER:
10665     {
10666       for (i = 0; i < MAX_PLAYERS; i++)
10667         if (action_arg_player_bits & (1 << i))
10668           PlayerWins(&stored_player[i]);
10669
10670       break;
10671     }
10672
10673     case CA_KILL_PLAYER:
10674     {
10675       for (i = 0; i < MAX_PLAYERS; i++)
10676         if (action_arg_player_bits & (1 << i))
10677           KillPlayer(&stored_player[i]);
10678
10679       break;
10680     }
10681
10682     case CA_SET_PLAYER_KEYS:
10683     {
10684       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10685       int element = getSpecialActionElement(action_arg_element,
10686                                             action_arg_number, EL_KEY_1);
10687
10688       if (IS_KEY(element))
10689       {
10690         for (i = 0; i < MAX_PLAYERS; i++)
10691         {
10692           if (trigger_player_bits & (1 << i))
10693           {
10694             stored_player[i].key[KEY_NR(element)] = key_state;
10695
10696             DrawGameDoorValues();
10697           }
10698         }
10699       }
10700
10701       break;
10702     }
10703
10704     case CA_SET_PLAYER_SPEED:
10705     {
10706 #if 0
10707       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10708 #endif
10709
10710       for (i = 0; i < MAX_PLAYERS; i++)
10711       {
10712         if (trigger_player_bits & (1 << i))
10713         {
10714           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10715
10716           if (action_arg == CA_ARG_SPEED_FASTER &&
10717               stored_player[i].cannot_move)
10718           {
10719             action_arg_number = STEPSIZE_VERY_SLOW;
10720           }
10721           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10722                    action_arg == CA_ARG_SPEED_FASTER)
10723           {
10724             action_arg_number = 2;
10725             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10726                            CA_MODE_MULTIPLY);
10727           }
10728           else if (action_arg == CA_ARG_NUMBER_RESET)
10729           {
10730             action_arg_number = level.initial_player_stepsize[i];
10731           }
10732
10733           move_stepsize =
10734             getModifiedActionNumber(move_stepsize,
10735                                     action_mode,
10736                                     action_arg_number,
10737                                     action_arg_number_min,
10738                                     action_arg_number_max);
10739
10740           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10741         }
10742       }
10743
10744       break;
10745     }
10746
10747     case CA_SET_PLAYER_SHIELD:
10748     {
10749       for (i = 0; i < MAX_PLAYERS; i++)
10750       {
10751         if (trigger_player_bits & (1 << i))
10752         {
10753           if (action_arg == CA_ARG_SHIELD_OFF)
10754           {
10755             stored_player[i].shield_normal_time_left = 0;
10756             stored_player[i].shield_deadly_time_left = 0;
10757           }
10758           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10759           {
10760             stored_player[i].shield_normal_time_left = 999999;
10761           }
10762           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10763           {
10764             stored_player[i].shield_normal_time_left = 999999;
10765             stored_player[i].shield_deadly_time_left = 999999;
10766           }
10767         }
10768       }
10769
10770       break;
10771     }
10772
10773 #if USE_PLAYER_GRAVITY
10774     case CA_SET_PLAYER_GRAVITY:
10775     {
10776       for (i = 0; i < MAX_PLAYERS; i++)
10777       {
10778         if (trigger_player_bits & (1 << i))
10779         {
10780           stored_player[i].gravity =
10781             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10782              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10783              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10784              stored_player[i].gravity);
10785         }
10786       }
10787
10788       break;
10789     }
10790 #endif
10791
10792     case CA_SET_PLAYER_ARTWORK:
10793     {
10794       for (i = 0; i < MAX_PLAYERS; i++)
10795       {
10796         if (trigger_player_bits & (1 << i))
10797         {
10798           int artwork_element = action_arg_element;
10799
10800           if (action_arg == CA_ARG_ELEMENT_RESET)
10801             artwork_element =
10802               (level.use_artwork_element[i] ? level.artwork_element[i] :
10803                stored_player[i].element_nr);
10804
10805 #if USE_GFX_RESET_PLAYER_ARTWORK
10806           if (stored_player[i].artwork_element != artwork_element)
10807             stored_player[i].Frame = 0;
10808 #endif
10809
10810           stored_player[i].artwork_element = artwork_element;
10811
10812           SetPlayerWaiting(&stored_player[i], FALSE);
10813
10814           /* set number of special actions for bored and sleeping animation */
10815           stored_player[i].num_special_action_bored =
10816             get_num_special_action(artwork_element,
10817                                    ACTION_BORING_1, ACTION_BORING_LAST);
10818           stored_player[i].num_special_action_sleeping =
10819             get_num_special_action(artwork_element,
10820                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10821         }
10822       }
10823
10824       break;
10825     }
10826
10827     case CA_SET_PLAYER_INVENTORY:
10828     {
10829       for (i = 0; i < MAX_PLAYERS; i++)
10830       {
10831         struct PlayerInfo *player = &stored_player[i];
10832         int j, k;
10833
10834         if (trigger_player_bits & (1 << i))
10835         {
10836           int inventory_element = action_arg_element;
10837
10838           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10839               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10840               action_arg == CA_ARG_ELEMENT_ACTION)
10841           {
10842             int element = inventory_element;
10843             int collect_count = element_info[element].collect_count_initial;
10844
10845             if (!IS_CUSTOM_ELEMENT(element))
10846               collect_count = 1;
10847
10848             if (collect_count == 0)
10849               player->inventory_infinite_element = element;
10850             else
10851               for (k = 0; k < collect_count; k++)
10852                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10853                   player->inventory_element[player->inventory_size++] =
10854                     element;
10855           }
10856           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10857                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10858                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10859           {
10860             if (player->inventory_infinite_element != EL_UNDEFINED &&
10861                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10862                                      action_arg_element_raw))
10863               player->inventory_infinite_element = EL_UNDEFINED;
10864
10865             for (k = 0, j = 0; j < player->inventory_size; j++)
10866             {
10867               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10868                                         action_arg_element_raw))
10869                 player->inventory_element[k++] = player->inventory_element[j];
10870             }
10871
10872             player->inventory_size = k;
10873           }
10874           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10875           {
10876             if (player->inventory_size > 0)
10877             {
10878               for (j = 0; j < player->inventory_size - 1; j++)
10879                 player->inventory_element[j] = player->inventory_element[j + 1];
10880
10881               player->inventory_size--;
10882             }
10883           }
10884           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10885           {
10886             if (player->inventory_size > 0)
10887               player->inventory_size--;
10888           }
10889           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10890           {
10891             player->inventory_infinite_element = EL_UNDEFINED;
10892             player->inventory_size = 0;
10893           }
10894           else if (action_arg == CA_ARG_INVENTORY_RESET)
10895           {
10896             player->inventory_infinite_element = EL_UNDEFINED;
10897             player->inventory_size = 0;
10898
10899             if (level.use_initial_inventory[i])
10900             {
10901               for (j = 0; j < level.initial_inventory_size[i]; j++)
10902               {
10903                 int element = level.initial_inventory_content[i][j];
10904                 int collect_count = element_info[element].collect_count_initial;
10905
10906                 if (!IS_CUSTOM_ELEMENT(element))
10907                   collect_count = 1;
10908
10909                 if (collect_count == 0)
10910                   player->inventory_infinite_element = element;
10911                 else
10912                   for (k = 0; k < collect_count; k++)
10913                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10914                       player->inventory_element[player->inventory_size++] =
10915                         element;
10916               }
10917             }
10918           }
10919         }
10920       }
10921
10922       break;
10923     }
10924
10925     /* ---------- CE actions  ---------------------------------------------- */
10926
10927     case CA_SET_CE_VALUE:
10928     {
10929 #if USE_NEW_CUSTOM_VALUE
10930       int last_ce_value = CustomValue[x][y];
10931
10932       CustomValue[x][y] = action_arg_number_new;
10933
10934       if (CustomValue[x][y] != last_ce_value)
10935       {
10936         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10937         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10938
10939         if (CustomValue[x][y] == 0)
10940         {
10941           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10942           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10943         }
10944       }
10945 #endif
10946
10947       break;
10948     }
10949
10950     case CA_SET_CE_SCORE:
10951     {
10952 #if USE_NEW_CUSTOM_VALUE
10953       int last_ce_score = ei->collect_score;
10954
10955       ei->collect_score = action_arg_number_new;
10956
10957       if (ei->collect_score != last_ce_score)
10958       {
10959         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10960         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10961
10962         if (ei->collect_score == 0)
10963         {
10964           int xx, yy;
10965
10966           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10967           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10968
10969           /*
10970             This is a very special case that seems to be a mixture between
10971             CheckElementChange() and CheckTriggeredElementChange(): while
10972             the first one only affects single elements that are triggered
10973             directly, the second one affects multiple elements in the playfield
10974             that are triggered indirectly by another element. This is a third
10975             case: Changing the CE score always affects multiple identical CEs,
10976             so every affected CE must be checked, not only the single CE for
10977             which the CE score was changed in the first place (as every instance
10978             of that CE shares the same CE score, and therefore also can change)!
10979           */
10980           SCAN_PLAYFIELD(xx, yy)
10981           {
10982             if (Feld[xx][yy] == element)
10983               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10984                                  CE_SCORE_GETS_ZERO);
10985           }
10986         }
10987       }
10988 #endif
10989
10990       break;
10991     }
10992
10993     case CA_SET_CE_ARTWORK:
10994     {
10995       int artwork_element = action_arg_element;
10996       boolean reset_frame = FALSE;
10997       int xx, yy;
10998
10999       if (action_arg == CA_ARG_ELEMENT_RESET)
11000         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11001                            element);
11002
11003       if (ei->gfx_element != artwork_element)
11004         reset_frame = TRUE;
11005
11006       ei->gfx_element = artwork_element;
11007
11008       SCAN_PLAYFIELD(xx, yy)
11009       {
11010         if (Feld[xx][yy] == element)
11011         {
11012           if (reset_frame)
11013           {
11014             ResetGfxAnimation(xx, yy);
11015             ResetRandomAnimationValue(xx, yy);
11016           }
11017
11018           TEST_DrawLevelField(xx, yy);
11019         }
11020       }
11021
11022       break;
11023     }
11024
11025     /* ---------- engine actions  ------------------------------------------ */
11026
11027     case CA_SET_ENGINE_SCAN_MODE:
11028     {
11029       InitPlayfieldScanMode(action_arg);
11030
11031       break;
11032     }
11033
11034     default:
11035       break;
11036   }
11037 }
11038
11039 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11040 {
11041   int old_element = Feld[x][y];
11042   int new_element = GetElementFromGroupElement(element);
11043   int previous_move_direction = MovDir[x][y];
11044 #if USE_NEW_CUSTOM_VALUE
11045   int last_ce_value = CustomValue[x][y];
11046 #endif
11047   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11048   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11049   boolean add_player_onto_element = (new_element_is_player &&
11050 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11051                                      /* this breaks SnakeBite when a snake is
11052                                         halfway through a door that closes */
11053                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11054                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11055 #endif
11056                                      IS_WALKABLE(old_element));
11057
11058 #if 0
11059   /* check if element under the player changes from accessible to unaccessible
11060      (needed for special case of dropping element which then changes) */
11061   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11062       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11063   {
11064     Bang(x, y);
11065
11066     return;
11067   }
11068 #endif
11069
11070   if (!add_player_onto_element)
11071   {
11072     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11073       RemoveMovingField(x, y);
11074     else
11075       RemoveField(x, y);
11076
11077     Feld[x][y] = new_element;
11078
11079 #if !USE_GFX_RESET_GFX_ANIMATION
11080     ResetGfxAnimation(x, y);
11081     ResetRandomAnimationValue(x, y);
11082 #endif
11083
11084     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11085       MovDir[x][y] = previous_move_direction;
11086
11087 #if USE_NEW_CUSTOM_VALUE
11088     if (element_info[new_element].use_last_ce_value)
11089       CustomValue[x][y] = last_ce_value;
11090 #endif
11091
11092     InitField_WithBug1(x, y, FALSE);
11093
11094     new_element = Feld[x][y];   /* element may have changed */
11095
11096 #if USE_GFX_RESET_GFX_ANIMATION
11097     ResetGfxAnimation(x, y);
11098     ResetRandomAnimationValue(x, y);
11099 #endif
11100
11101     TEST_DrawLevelField(x, y);
11102
11103     if (GFX_CRUMBLED(new_element))
11104       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11105   }
11106
11107 #if 1
11108   /* check if element under the player changes from accessible to unaccessible
11109      (needed for special case of dropping element which then changes) */
11110   /* (must be checked after creating new element for walkable group elements) */
11111 #if USE_FIX_KILLED_BY_NON_WALKABLE
11112   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11113       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11114   {
11115     Bang(x, y);
11116
11117     return;
11118   }
11119 #else
11120   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11121       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11122   {
11123     Bang(x, y);
11124
11125     return;
11126   }
11127 #endif
11128 #endif
11129
11130   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11131   if (new_element_is_player)
11132     RelocatePlayer(x, y, new_element);
11133
11134   if (is_change)
11135     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11136
11137   TestIfBadThingTouchesPlayer(x, y);
11138   TestIfPlayerTouchesCustomElement(x, y);
11139   TestIfElementTouchesCustomElement(x, y);
11140 }
11141
11142 static void CreateField(int x, int y, int element)
11143 {
11144   CreateFieldExt(x, y, element, FALSE);
11145 }
11146
11147 static void CreateElementFromChange(int x, int y, int element)
11148 {
11149   element = GET_VALID_RUNTIME_ELEMENT(element);
11150
11151 #if USE_STOP_CHANGED_ELEMENTS
11152   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11153   {
11154     int old_element = Feld[x][y];
11155
11156     /* prevent changed element from moving in same engine frame
11157        unless both old and new element can either fall or move */
11158     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11159         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11160       Stop[x][y] = TRUE;
11161   }
11162 #endif
11163
11164   CreateFieldExt(x, y, element, TRUE);
11165 }
11166
11167 static boolean ChangeElement(int x, int y, int element, int page)
11168 {
11169   struct ElementInfo *ei = &element_info[element];
11170   struct ElementChangeInfo *change = &ei->change_page[page];
11171   int ce_value = CustomValue[x][y];
11172   int ce_score = ei->collect_score;
11173   int target_element;
11174   int old_element = Feld[x][y];
11175
11176   /* always use default change event to prevent running into a loop */
11177   if (ChangeEvent[x][y] == -1)
11178     ChangeEvent[x][y] = CE_DELAY;
11179
11180   if (ChangeEvent[x][y] == CE_DELAY)
11181   {
11182     /* reset actual trigger element, trigger player and action element */
11183     change->actual_trigger_element = EL_EMPTY;
11184     change->actual_trigger_player = EL_EMPTY;
11185     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11186     change->actual_trigger_side = CH_SIDE_NONE;
11187     change->actual_trigger_ce_value = 0;
11188     change->actual_trigger_ce_score = 0;
11189   }
11190
11191   /* do not change elements more than a specified maximum number of changes */
11192   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11193     return FALSE;
11194
11195   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11196
11197   if (change->explode)
11198   {
11199     Bang(x, y);
11200
11201     return TRUE;
11202   }
11203
11204   if (change->use_target_content)
11205   {
11206     boolean complete_replace = TRUE;
11207     boolean can_replace[3][3];
11208     int xx, yy;
11209
11210     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11211     {
11212       boolean is_empty;
11213       boolean is_walkable;
11214       boolean is_diggable;
11215       boolean is_collectible;
11216       boolean is_removable;
11217       boolean is_destructible;
11218       int ex = x + xx - 1;
11219       int ey = y + yy - 1;
11220       int content_element = change->target_content.e[xx][yy];
11221       int e;
11222
11223       can_replace[xx][yy] = TRUE;
11224
11225       if (ex == x && ey == y)   /* do not check changing element itself */
11226         continue;
11227
11228       if (content_element == EL_EMPTY_SPACE)
11229       {
11230         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11231
11232         continue;
11233       }
11234
11235       if (!IN_LEV_FIELD(ex, ey))
11236       {
11237         can_replace[xx][yy] = FALSE;
11238         complete_replace = FALSE;
11239
11240         continue;
11241       }
11242
11243       e = Feld[ex][ey];
11244
11245       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11246         e = MovingOrBlocked2Element(ex, ey);
11247
11248       is_empty = (IS_FREE(ex, ey) ||
11249                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11250
11251       is_walkable     = (is_empty || IS_WALKABLE(e));
11252       is_diggable     = (is_empty || IS_DIGGABLE(e));
11253       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11254       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11255       is_removable    = (is_diggable || is_collectible);
11256
11257       can_replace[xx][yy] =
11258         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11259           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11260           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11261           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11262           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11263           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11264          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11265
11266       if (!can_replace[xx][yy])
11267         complete_replace = FALSE;
11268     }
11269
11270     if (!change->only_if_complete || complete_replace)
11271     {
11272       boolean something_has_changed = FALSE;
11273
11274       if (change->only_if_complete && change->use_random_replace &&
11275           RND(100) < change->random_percentage)
11276         return FALSE;
11277
11278       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11279       {
11280         int ex = x + xx - 1;
11281         int ey = y + yy - 1;
11282         int content_element;
11283
11284         if (can_replace[xx][yy] && (!change->use_random_replace ||
11285                                     RND(100) < change->random_percentage))
11286         {
11287           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11288             RemoveMovingField(ex, ey);
11289
11290           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11291
11292           content_element = change->target_content.e[xx][yy];
11293           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11294                                               ce_value, ce_score);
11295
11296           CreateElementFromChange(ex, ey, target_element);
11297
11298           something_has_changed = TRUE;
11299
11300           /* for symmetry reasons, freeze newly created border elements */
11301           if (ex != x || ey != y)
11302             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11303         }
11304       }
11305
11306       if (something_has_changed)
11307       {
11308         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11309         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11310       }
11311     }
11312   }
11313   else
11314   {
11315     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11316                                         ce_value, ce_score);
11317
11318     if (element == EL_DIAGONAL_GROWING ||
11319         element == EL_DIAGONAL_SHRINKING)
11320     {
11321       target_element = Store[x][y];
11322
11323       Store[x][y] = EL_EMPTY;
11324     }
11325
11326     CreateElementFromChange(x, y, target_element);
11327
11328     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11329     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11330   }
11331
11332   /* this uses direct change before indirect change */
11333   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11334
11335   return TRUE;
11336 }
11337
11338 #if USE_NEW_DELAYED_ACTION
11339
11340 static void HandleElementChange(int x, int y, int page)
11341 {
11342   int element = MovingOrBlocked2Element(x, y);
11343   struct ElementInfo *ei = &element_info[element];
11344   struct ElementChangeInfo *change = &ei->change_page[page];
11345   boolean handle_action_before_change = FALSE;
11346
11347 #ifdef DEBUG
11348   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11349       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11350   {
11351     printf("\n\n");
11352     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11353            x, y, element, element_info[element].token_name);
11354     printf("HandleElementChange(): This should never happen!\n");
11355     printf("\n\n");
11356   }
11357 #endif
11358
11359   /* this can happen with classic bombs on walkable, changing elements */
11360   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11361   {
11362 #if 0
11363     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11364       ChangeDelay[x][y] = 0;
11365 #endif
11366
11367     return;
11368   }
11369
11370   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11371   {
11372     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11373
11374     if (change->can_change)
11375     {
11376 #if 1
11377       /* !!! not clear why graphic animation should be reset at all here !!! */
11378       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11379 #if USE_GFX_RESET_WHEN_NOT_MOVING
11380       /* when a custom element is about to change (for example by change delay),
11381          do not reset graphic animation when the custom element is moving */
11382       if (!IS_MOVING(x, y))
11383 #endif
11384       {
11385         ResetGfxAnimation(x, y);
11386         ResetRandomAnimationValue(x, y);
11387       }
11388 #endif
11389
11390       if (change->pre_change_function)
11391         change->pre_change_function(x, y);
11392     }
11393   }
11394
11395   ChangeDelay[x][y]--;
11396
11397   if (ChangeDelay[x][y] != 0)           /* continue element change */
11398   {
11399     if (change->can_change)
11400     {
11401       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11402
11403       if (IS_ANIMATED(graphic))
11404         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11405
11406       if (change->change_function)
11407         change->change_function(x, y);
11408     }
11409   }
11410   else                                  /* finish element change */
11411   {
11412     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11413     {
11414       page = ChangePage[x][y];
11415       ChangePage[x][y] = -1;
11416
11417       change = &ei->change_page[page];
11418     }
11419
11420     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11421     {
11422       ChangeDelay[x][y] = 1;            /* try change after next move step */
11423       ChangePage[x][y] = page;          /* remember page to use for change */
11424
11425       return;
11426     }
11427
11428 #if 1
11429     /* special case: set new level random seed before changing element */
11430     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11431       handle_action_before_change = TRUE;
11432
11433     if (change->has_action && handle_action_before_change)
11434       ExecuteCustomElementAction(x, y, element, page);
11435 #endif
11436
11437     if (change->can_change)
11438     {
11439       if (ChangeElement(x, y, element, page))
11440       {
11441         if (change->post_change_function)
11442           change->post_change_function(x, y);
11443       }
11444     }
11445
11446     if (change->has_action && !handle_action_before_change)
11447       ExecuteCustomElementAction(x, y, element, page);
11448   }
11449 }
11450
11451 #else
11452
11453 static void HandleElementChange(int x, int y, int page)
11454 {
11455   int element = MovingOrBlocked2Element(x, y);
11456   struct ElementInfo *ei = &element_info[element];
11457   struct ElementChangeInfo *change = &ei->change_page[page];
11458
11459 #ifdef DEBUG
11460   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11461   {
11462     printf("\n\n");
11463     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11464            x, y, element, element_info[element].token_name);
11465     printf("HandleElementChange(): This should never happen!\n");
11466     printf("\n\n");
11467   }
11468 #endif
11469
11470   /* this can happen with classic bombs on walkable, changing elements */
11471   if (!CAN_CHANGE(element))
11472   {
11473 #if 0
11474     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11475       ChangeDelay[x][y] = 0;
11476 #endif
11477
11478     return;
11479   }
11480
11481   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11482   {
11483     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11484
11485     ResetGfxAnimation(x, y);
11486     ResetRandomAnimationValue(x, y);
11487
11488     if (change->pre_change_function)
11489       change->pre_change_function(x, y);
11490   }
11491
11492   ChangeDelay[x][y]--;
11493
11494   if (ChangeDelay[x][y] != 0)           /* continue element change */
11495   {
11496     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11497
11498     if (IS_ANIMATED(graphic))
11499       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11500
11501     if (change->change_function)
11502       change->change_function(x, y);
11503   }
11504   else                                  /* finish element change */
11505   {
11506     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11507     {
11508       page = ChangePage[x][y];
11509       ChangePage[x][y] = -1;
11510
11511       change = &ei->change_page[page];
11512     }
11513
11514     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11515     {
11516       ChangeDelay[x][y] = 1;            /* try change after next move step */
11517       ChangePage[x][y] = page;          /* remember page to use for change */
11518
11519       return;
11520     }
11521
11522     if (ChangeElement(x, y, element, page))
11523     {
11524       if (change->post_change_function)
11525         change->post_change_function(x, y);
11526     }
11527   }
11528 }
11529
11530 #endif
11531
11532 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11533                                               int trigger_element,
11534                                               int trigger_event,
11535                                               int trigger_player,
11536                                               int trigger_side,
11537                                               int trigger_page)
11538 {
11539   boolean change_done_any = FALSE;
11540   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11541   int i;
11542
11543   if (!(trigger_events[trigger_element][trigger_event]))
11544     return FALSE;
11545
11546 #if 0
11547   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11548          trigger_event, recursion_loop_depth, recursion_loop_detected,
11549          recursion_loop_element, EL_NAME(recursion_loop_element));
11550 #endif
11551
11552   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11553
11554   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11555   {
11556     int element = EL_CUSTOM_START + i;
11557     boolean change_done = FALSE;
11558     int p;
11559
11560     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11561         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11562       continue;
11563
11564     for (p = 0; p < element_info[element].num_change_pages; p++)
11565     {
11566       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11567
11568       if (change->can_change_or_has_action &&
11569           change->has_event[trigger_event] &&
11570           change->trigger_side & trigger_side &&
11571           change->trigger_player & trigger_player &&
11572           change->trigger_page & trigger_page_bits &&
11573           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11574       {
11575         change->actual_trigger_element = trigger_element;
11576         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11577         change->actual_trigger_player_bits = trigger_player;
11578         change->actual_trigger_side = trigger_side;
11579         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11580         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11581
11582 #if 0
11583         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11584                element, EL_NAME(element), p);
11585 #endif
11586
11587         if ((change->can_change && !change_done) || change->has_action)
11588         {
11589           int x, y;
11590
11591           SCAN_PLAYFIELD(x, y)
11592           {
11593             if (Feld[x][y] == element)
11594             {
11595               if (change->can_change && !change_done)
11596               {
11597 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11598                 /* if element already changed in this frame, not only prevent
11599                    another element change (checked in ChangeElement()), but
11600                    also prevent additional element actions for this element */
11601
11602                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11603                     !level.use_action_after_change_bug)
11604                   continue;
11605 #endif
11606
11607 #if 0
11608                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11609                        element, EL_NAME(element), p);
11610 #endif
11611
11612                 ChangeDelay[x][y] = 1;
11613                 ChangeEvent[x][y] = trigger_event;
11614
11615                 HandleElementChange(x, y, p);
11616               }
11617 #if USE_NEW_DELAYED_ACTION
11618               else if (change->has_action)
11619               {
11620 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11621                 /* if element already changed in this frame, not only prevent
11622                    another element change (checked in ChangeElement()), but
11623                    also prevent additional element actions for this element */
11624
11625                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11626                     !level.use_action_after_change_bug)
11627                   continue;
11628 #endif
11629
11630
11631 #if 0
11632                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11633                        element, EL_NAME(element), p);
11634 #endif
11635
11636                 ExecuteCustomElementAction(x, y, element, p);
11637                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11638               }
11639 #else
11640               if (change->has_action)
11641               {
11642                 ExecuteCustomElementAction(x, y, element, p);
11643                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11644               }
11645 #endif
11646             }
11647           }
11648
11649           if (change->can_change)
11650           {
11651             change_done = TRUE;
11652             change_done_any = TRUE;
11653
11654 #if 0
11655             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11656                    element, EL_NAME(element), p);
11657 #endif
11658
11659           }
11660         }
11661       }
11662     }
11663   }
11664
11665   RECURSION_LOOP_DETECTION_END();
11666
11667   return change_done_any;
11668 }
11669
11670 static boolean CheckElementChangeExt(int x, int y,
11671                                      int element,
11672                                      int trigger_element,
11673                                      int trigger_event,
11674                                      int trigger_player,
11675                                      int trigger_side)
11676 {
11677   boolean change_done = FALSE;
11678   int p;
11679
11680   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11681       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11682     return FALSE;
11683
11684   if (Feld[x][y] == EL_BLOCKED)
11685   {
11686     Blocked2Moving(x, y, &x, &y);
11687     element = Feld[x][y];
11688   }
11689
11690 #if 0
11691   /* check if element has already changed */
11692   if (Feld[x][y] != element)
11693     return FALSE;
11694 #else
11695   /* check if element has already changed or is about to change after moving */
11696   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11697        Feld[x][y] != element) ||
11698
11699       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11700        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11701         ChangePage[x][y] != -1)))
11702     return FALSE;
11703 #endif
11704
11705 #if 0
11706   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11707          trigger_event, recursion_loop_depth, recursion_loop_detected,
11708          recursion_loop_element, EL_NAME(recursion_loop_element));
11709 #endif
11710
11711   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11712
11713 #if 0
11714   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11715 #endif
11716
11717   for (p = 0; p < element_info[element].num_change_pages; p++)
11718   {
11719     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11720
11721     /* check trigger element for all events where the element that is checked
11722        for changing interacts with a directly adjacent element -- this is
11723        different to element changes that affect other elements to change on the
11724        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11725     boolean check_trigger_element =
11726       (trigger_event == CE_TOUCHING_X ||
11727        trigger_event == CE_HITTING_X ||
11728        trigger_event == CE_HIT_BY_X ||
11729 #if 1
11730        /* this one was forgotten until 3.2.3 */
11731        trigger_event == CE_DIGGING_X);
11732 #endif
11733
11734     if (change->can_change_or_has_action &&
11735         change->has_event[trigger_event] &&
11736         change->trigger_side & trigger_side &&
11737         change->trigger_player & trigger_player &&
11738         (!check_trigger_element ||
11739          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11740     {
11741       change->actual_trigger_element = trigger_element;
11742       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11743       change->actual_trigger_player_bits = trigger_player;
11744       change->actual_trigger_side = trigger_side;
11745       change->actual_trigger_ce_value = CustomValue[x][y];
11746       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11747
11748       /* special case: trigger element not at (x,y) position for some events */
11749       if (check_trigger_element)
11750       {
11751         static struct
11752         {
11753           int dx, dy;
11754         } move_xy[] =
11755           {
11756             {  0,  0 },
11757             { -1,  0 },
11758             { +1,  0 },
11759             {  0,  0 },
11760             {  0, -1 },
11761             {  0,  0 }, { 0, 0 }, { 0, 0 },
11762             {  0, +1 }
11763           };
11764
11765         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11766         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11767
11768         change->actual_trigger_ce_value = CustomValue[xx][yy];
11769         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11770       }
11771
11772       if (change->can_change && !change_done)
11773       {
11774         ChangeDelay[x][y] = 1;
11775         ChangeEvent[x][y] = trigger_event;
11776
11777         HandleElementChange(x, y, p);
11778
11779         change_done = TRUE;
11780       }
11781 #if USE_NEW_DELAYED_ACTION
11782       else if (change->has_action)
11783       {
11784         ExecuteCustomElementAction(x, y, element, p);
11785         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11786       }
11787 #else
11788       if (change->has_action)
11789       {
11790         ExecuteCustomElementAction(x, y, element, p);
11791         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11792       }
11793 #endif
11794     }
11795   }
11796
11797   RECURSION_LOOP_DETECTION_END();
11798
11799   return change_done;
11800 }
11801
11802 static void PlayPlayerSound(struct PlayerInfo *player)
11803 {
11804   int jx = player->jx, jy = player->jy;
11805   int sound_element = player->artwork_element;
11806   int last_action = player->last_action_waiting;
11807   int action = player->action_waiting;
11808
11809   if (player->is_waiting)
11810   {
11811     if (action != last_action)
11812       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11813     else
11814       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11815   }
11816   else
11817   {
11818     if (action != last_action)
11819       StopSound(element_info[sound_element].sound[last_action]);
11820
11821     if (last_action == ACTION_SLEEPING)
11822       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11823   }
11824 }
11825
11826 static void PlayAllPlayersSound()
11827 {
11828   int i;
11829
11830   for (i = 0; i < MAX_PLAYERS; i++)
11831     if (stored_player[i].active)
11832       PlayPlayerSound(&stored_player[i]);
11833 }
11834
11835 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11836 {
11837   boolean last_waiting = player->is_waiting;
11838   int move_dir = player->MovDir;
11839
11840   player->dir_waiting = move_dir;
11841   player->last_action_waiting = player->action_waiting;
11842
11843   if (is_waiting)
11844   {
11845     if (!last_waiting)          /* not waiting -> waiting */
11846     {
11847       player->is_waiting = TRUE;
11848
11849       player->frame_counter_bored =
11850         FrameCounter +
11851         game.player_boring_delay_fixed +
11852         GetSimpleRandom(game.player_boring_delay_random);
11853       player->frame_counter_sleeping =
11854         FrameCounter +
11855         game.player_sleeping_delay_fixed +
11856         GetSimpleRandom(game.player_sleeping_delay_random);
11857
11858       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11859     }
11860
11861     if (game.player_sleeping_delay_fixed +
11862         game.player_sleeping_delay_random > 0 &&
11863         player->anim_delay_counter == 0 &&
11864         player->post_delay_counter == 0 &&
11865         FrameCounter >= player->frame_counter_sleeping)
11866       player->is_sleeping = TRUE;
11867     else if (game.player_boring_delay_fixed +
11868              game.player_boring_delay_random > 0 &&
11869              FrameCounter >= player->frame_counter_bored)
11870       player->is_bored = TRUE;
11871
11872     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11873                               player->is_bored ? ACTION_BORING :
11874                               ACTION_WAITING);
11875
11876     if (player->is_sleeping && player->use_murphy)
11877     {
11878       /* special case for sleeping Murphy when leaning against non-free tile */
11879
11880       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11881           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11882            !IS_MOVING(player->jx - 1, player->jy)))
11883         move_dir = MV_LEFT;
11884       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11885                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11886                 !IS_MOVING(player->jx + 1, player->jy)))
11887         move_dir = MV_RIGHT;
11888       else
11889         player->is_sleeping = FALSE;
11890
11891       player->dir_waiting = move_dir;
11892     }
11893
11894     if (player->is_sleeping)
11895     {
11896       if (player->num_special_action_sleeping > 0)
11897       {
11898         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11899         {
11900           int last_special_action = player->special_action_sleeping;
11901           int num_special_action = player->num_special_action_sleeping;
11902           int special_action =
11903             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11904              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11905              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11906              last_special_action + 1 : ACTION_SLEEPING);
11907           int special_graphic =
11908             el_act_dir2img(player->artwork_element, special_action, move_dir);
11909
11910           player->anim_delay_counter =
11911             graphic_info[special_graphic].anim_delay_fixed +
11912             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11913           player->post_delay_counter =
11914             graphic_info[special_graphic].post_delay_fixed +
11915             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11916
11917           player->special_action_sleeping = special_action;
11918         }
11919
11920         if (player->anim_delay_counter > 0)
11921         {
11922           player->action_waiting = player->special_action_sleeping;
11923           player->anim_delay_counter--;
11924         }
11925         else if (player->post_delay_counter > 0)
11926         {
11927           player->post_delay_counter--;
11928         }
11929       }
11930     }
11931     else if (player->is_bored)
11932     {
11933       if (player->num_special_action_bored > 0)
11934       {
11935         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11936         {
11937           int special_action =
11938             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11939           int special_graphic =
11940             el_act_dir2img(player->artwork_element, special_action, move_dir);
11941
11942           player->anim_delay_counter =
11943             graphic_info[special_graphic].anim_delay_fixed +
11944             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11945           player->post_delay_counter =
11946             graphic_info[special_graphic].post_delay_fixed +
11947             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11948
11949           player->special_action_bored = special_action;
11950         }
11951
11952         if (player->anim_delay_counter > 0)
11953         {
11954           player->action_waiting = player->special_action_bored;
11955           player->anim_delay_counter--;
11956         }
11957         else if (player->post_delay_counter > 0)
11958         {
11959           player->post_delay_counter--;
11960         }
11961       }
11962     }
11963   }
11964   else if (last_waiting)        /* waiting -> not waiting */
11965   {
11966     player->is_waiting = FALSE;
11967     player->is_bored = FALSE;
11968     player->is_sleeping = FALSE;
11969
11970     player->frame_counter_bored = -1;
11971     player->frame_counter_sleeping = -1;
11972
11973     player->anim_delay_counter = 0;
11974     player->post_delay_counter = 0;
11975
11976     player->dir_waiting = player->MovDir;
11977     player->action_waiting = ACTION_DEFAULT;
11978
11979     player->special_action_bored = ACTION_DEFAULT;
11980     player->special_action_sleeping = ACTION_DEFAULT;
11981   }
11982 }
11983
11984 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11985 {
11986   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11987   int left      = player_action & JOY_LEFT;
11988   int right     = player_action & JOY_RIGHT;
11989   int up        = player_action & JOY_UP;
11990   int down      = player_action & JOY_DOWN;
11991   int button1   = player_action & JOY_BUTTON_1;
11992   int button2   = player_action & JOY_BUTTON_2;
11993   int dx        = (left ? -1 : right ? 1 : 0);
11994   int dy        = (up   ? -1 : down  ? 1 : 0);
11995
11996   if (!player->active || tape.pausing)
11997     return 0;
11998
11999   if (player_action)
12000   {
12001     if (button1)
12002       snapped = SnapField(player, dx, dy);
12003     else
12004     {
12005       if (button2)
12006         dropped = DropElement(player);
12007
12008       moved = MovePlayer(player, dx, dy);
12009     }
12010
12011     if (tape.single_step && tape.recording && !tape.pausing)
12012     {
12013 #if 1
12014       /* as it is called "single step mode", just return to pause mode when the
12015          player stopped moving after one tile (or never starts moving at all) */
12016       if (!player->is_moving)
12017 #else
12018       /* this is buggy: there are quite some cases where the single step mode
12019          does not return to pause mode (like pushing things that don't move
12020          or simply by trying to run against a wall) */
12021       if (button1 || (dropped && !moved))
12022 #endif
12023       {
12024         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12025         SnapField(player, 0, 0);                /* stop snapping */
12026       }
12027     }
12028
12029     SetPlayerWaiting(player, FALSE);
12030
12031     return player_action;
12032   }
12033   else
12034   {
12035     /* no actions for this player (no input at player's configured device) */
12036
12037     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12038     SnapField(player, 0, 0);
12039     CheckGravityMovementWhenNotMoving(player);
12040
12041     if (player->MovPos == 0)
12042       SetPlayerWaiting(player, TRUE);
12043
12044     if (player->MovPos == 0)    /* needed for tape.playing */
12045       player->is_moving = FALSE;
12046
12047     player->is_dropping = FALSE;
12048     player->is_dropping_pressed = FALSE;
12049     player->drop_pressed_delay = 0;
12050
12051     return 0;
12052   }
12053 }
12054
12055 static void CheckLevelTime()
12056 {
12057   int i;
12058
12059   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12060   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12061   {
12062     if (level.native_em_level->lev->home == 0)  /* all players at home */
12063     {
12064       PlayerWins(local_player);
12065
12066       AllPlayersGone = TRUE;
12067
12068       level.native_em_level->lev->home = -1;
12069     }
12070
12071     if (level.native_em_level->ply[0]->alive == 0 &&
12072         level.native_em_level->ply[1]->alive == 0 &&
12073         level.native_em_level->ply[2]->alive == 0 &&
12074         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12075       AllPlayersGone = TRUE;
12076   }
12077   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12078   {
12079     if (game_sp.LevelSolved &&
12080         !game_sp.GameOver)                              /* game won */
12081     {
12082       PlayerWins(local_player);
12083
12084       game_sp.GameOver = TRUE;
12085
12086       AllPlayersGone = TRUE;
12087     }
12088
12089     if (game_sp.GameOver)                               /* game lost */
12090       AllPlayersGone = TRUE;
12091   }
12092
12093   if (TimeFrames >= FRAMES_PER_SECOND)
12094   {
12095     TimeFrames = 0;
12096     TapeTime++;
12097
12098     for (i = 0; i < MAX_PLAYERS; i++)
12099     {
12100       struct PlayerInfo *player = &stored_player[i];
12101
12102       if (SHIELD_ON(player))
12103       {
12104         player->shield_normal_time_left--;
12105
12106         if (player->shield_deadly_time_left > 0)
12107           player->shield_deadly_time_left--;
12108       }
12109     }
12110
12111     if (!local_player->LevelSolved && !level.use_step_counter)
12112     {
12113       TimePlayed++;
12114
12115       if (TimeLeft > 0)
12116       {
12117         TimeLeft--;
12118
12119         if (TimeLeft <= 10 && setup.time_limit)
12120           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12121
12122 #if 1
12123         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12124
12125         DisplayGameControlValues();
12126 #else
12127         DrawGameValue_Time(TimeLeft);
12128 #endif
12129
12130         if (!TimeLeft && setup.time_limit)
12131         {
12132           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12133             level.native_em_level->lev->killed_out_of_time = TRUE;
12134           else
12135             for (i = 0; i < MAX_PLAYERS; i++)
12136               KillPlayer(&stored_player[i]);
12137         }
12138       }
12139 #if 1
12140       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12141       {
12142         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12143
12144         DisplayGameControlValues();
12145       }
12146 #else
12147       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12148         DrawGameValue_Time(TimePlayed);
12149 #endif
12150
12151       level.native_em_level->lev->time =
12152         (level.time == 0 ? TimePlayed : TimeLeft);
12153     }
12154
12155     if (tape.recording || tape.playing)
12156       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12157   }
12158
12159 #if 1
12160   UpdateAndDisplayGameControlValues();
12161 #else
12162   UpdateGameDoorValues();
12163   DrawGameDoorValues();
12164 #endif
12165 }
12166
12167 void AdvanceFrameAndPlayerCounters(int player_nr)
12168 {
12169   int i;
12170
12171   /* advance frame counters (global frame counter and time frame counter) */
12172   FrameCounter++;
12173   TimeFrames++;
12174
12175   /* advance player counters (counters for move delay, move animation etc.) */
12176   for (i = 0; i < MAX_PLAYERS; i++)
12177   {
12178     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12179     int move_delay_value = stored_player[i].move_delay_value;
12180     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12181
12182     if (!advance_player_counters)       /* not all players may be affected */
12183       continue;
12184
12185 #if USE_NEW_PLAYER_ANIM
12186     if (move_frames == 0)       /* less than one move per game frame */
12187     {
12188       int stepsize = TILEX / move_delay_value;
12189       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12190       int count = (stored_player[i].is_moving ?
12191                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12192
12193       if (count % delay == 0)
12194         move_frames = 1;
12195     }
12196 #endif
12197
12198     stored_player[i].Frame += move_frames;
12199
12200     if (stored_player[i].MovPos != 0)
12201       stored_player[i].StepFrame += move_frames;
12202
12203     if (stored_player[i].move_delay > 0)
12204       stored_player[i].move_delay--;
12205
12206     /* due to bugs in previous versions, counter must count up, not down */
12207     if (stored_player[i].push_delay != -1)
12208       stored_player[i].push_delay++;
12209
12210     if (stored_player[i].drop_delay > 0)
12211       stored_player[i].drop_delay--;
12212
12213     if (stored_player[i].is_dropping_pressed)
12214       stored_player[i].drop_pressed_delay++;
12215   }
12216 }
12217
12218 void StartGameActions(boolean init_network_game, boolean record_tape,
12219                       long random_seed)
12220 {
12221   unsigned long new_random_seed = InitRND(random_seed);
12222
12223   if (record_tape)
12224     TapeStartRecording(new_random_seed);
12225
12226 #if defined(NETWORK_AVALIABLE)
12227   if (init_network_game)
12228   {
12229     SendToServer_StartPlaying();
12230
12231     return;
12232   }
12233 #endif
12234
12235   InitGame();
12236 }
12237
12238 void GameActions()
12239 {
12240   static unsigned long game_frame_delay = 0;
12241   unsigned long game_frame_delay_value;
12242   byte *recorded_player_action;
12243   byte summarized_player_action = 0;
12244   byte tape_action[MAX_PLAYERS];
12245   int i;
12246
12247   /* detect endless loops, caused by custom element programming */
12248   if (recursion_loop_detected && recursion_loop_depth == 0)
12249   {
12250     char *message = getStringCat3("Internal Error ! Element ",
12251                                   EL_NAME(recursion_loop_element),
12252                                   " caused endless loop ! Quit the game ?");
12253
12254     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12255           EL_NAME(recursion_loop_element));
12256
12257     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12258
12259     recursion_loop_detected = FALSE;    /* if game should be continued */
12260
12261     free(message);
12262
12263     return;
12264   }
12265
12266   if (game.restart_level)
12267     StartGameActions(options.network, setup.autorecord, level.random_seed);
12268
12269   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12270   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12271   {
12272     if (level.native_em_level->lev->home == 0)  /* all players at home */
12273     {
12274       PlayerWins(local_player);
12275
12276       AllPlayersGone = TRUE;
12277
12278       level.native_em_level->lev->home = -1;
12279     }
12280
12281     if (level.native_em_level->ply[0]->alive == 0 &&
12282         level.native_em_level->ply[1]->alive == 0 &&
12283         level.native_em_level->ply[2]->alive == 0 &&
12284         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12285       AllPlayersGone = TRUE;
12286   }
12287   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12288   {
12289     if (game_sp.LevelSolved &&
12290         !game_sp.GameOver)                              /* game won */
12291     {
12292       PlayerWins(local_player);
12293
12294       game_sp.GameOver = TRUE;
12295
12296       AllPlayersGone = TRUE;
12297     }
12298
12299     if (game_sp.GameOver)                               /* game lost */
12300       AllPlayersGone = TRUE;
12301   }
12302
12303   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12304     GameWon();
12305
12306   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12307     TapeStop();
12308
12309   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12310     return;
12311
12312   game_frame_delay_value =
12313     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12314
12315   if (tape.playing && tape.warp_forward && !tape.pausing)
12316     game_frame_delay_value = 0;
12317
12318   /* ---------- main game synchronization point ---------- */
12319
12320   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12321
12322   if (network_playing && !network_player_action_received)
12323   {
12324     /* try to get network player actions in time */
12325
12326 #if defined(NETWORK_AVALIABLE)
12327     /* last chance to get network player actions without main loop delay */
12328     HandleNetworking();
12329 #endif
12330
12331     /* game was quit by network peer */
12332     if (game_status != GAME_MODE_PLAYING)
12333       return;
12334
12335     if (!network_player_action_received)
12336       return;           /* failed to get network player actions in time */
12337
12338     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12339   }
12340
12341   if (tape.pausing)
12342     return;
12343
12344   /* at this point we know that we really continue executing the game */
12345
12346   network_player_action_received = FALSE;
12347
12348   /* when playing tape, read previously recorded player input from tape data */
12349   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12350
12351 #if 1
12352   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12353   if (tape.pausing)
12354     return;
12355 #endif
12356
12357   if (tape.set_centered_player)
12358   {
12359     game.centered_player_nr_next = tape.centered_player_nr_next;
12360     game.set_centered_player = TRUE;
12361   }
12362
12363   for (i = 0; i < MAX_PLAYERS; i++)
12364   {
12365     summarized_player_action |= stored_player[i].action;
12366
12367     if (!network_playing)
12368       stored_player[i].effective_action = stored_player[i].action;
12369   }
12370
12371 #if defined(NETWORK_AVALIABLE)
12372   if (network_playing)
12373     SendToServer_MovePlayer(summarized_player_action);
12374 #endif
12375
12376   if (!options.network && !setup.team_mode)
12377     local_player->effective_action = summarized_player_action;
12378
12379   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12380   {
12381     for (i = 0; i < MAX_PLAYERS; i++)
12382       stored_player[i].effective_action =
12383         (i == game.centered_player_nr ? summarized_player_action : 0);
12384   }
12385
12386   if (recorded_player_action != NULL)
12387     for (i = 0; i < MAX_PLAYERS; i++)
12388       stored_player[i].effective_action = recorded_player_action[i];
12389
12390   for (i = 0; i < MAX_PLAYERS; i++)
12391   {
12392     tape_action[i] = stored_player[i].effective_action;
12393
12394     /* (this can only happen in the R'n'D game engine) */
12395     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12396       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12397   }
12398
12399   /* only record actions from input devices, but not programmed actions */
12400   if (tape.recording)
12401     TapeRecordAction(tape_action);
12402
12403 #if USE_NEW_PLAYER_ASSIGNMENTS
12404   {
12405     byte mapped_action[MAX_PLAYERS];
12406
12407     for (i = 0; i < MAX_PLAYERS; i++)
12408       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12409
12410     for (i = 0; i < MAX_PLAYERS; i++)
12411       stored_player[i].effective_action = mapped_action[i];
12412   }
12413 #endif
12414
12415   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12416   {
12417     GameActions_EM_Main();
12418   }
12419   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12420   {
12421     GameActions_SP_Main();
12422   }
12423   else
12424   {
12425     GameActions_RND();
12426   }
12427 }
12428
12429 void GameActions_EM_Main()
12430 {
12431   byte effective_action[MAX_PLAYERS];
12432   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12433   int i;
12434
12435   for (i = 0; i < MAX_PLAYERS; i++)
12436     effective_action[i] = stored_player[i].effective_action;
12437
12438   GameActions_EM(effective_action, warp_mode);
12439
12440   CheckLevelTime();
12441
12442   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12443 }
12444
12445 void GameActions_SP_Main()
12446 {
12447   byte effective_action[MAX_PLAYERS];
12448   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12449   int i;
12450
12451   for (i = 0; i < MAX_PLAYERS; i++)
12452     effective_action[i] = stored_player[i].effective_action;
12453
12454   GameActions_SP(effective_action, warp_mode);
12455
12456   CheckLevelTime();
12457
12458   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12459 }
12460
12461 void GameActions_RND()
12462 {
12463   int magic_wall_x = 0, magic_wall_y = 0;
12464   int i, x, y, element, graphic;
12465
12466   InitPlayfieldScanModeVars();
12467
12468 #if USE_ONE_MORE_CHANGE_PER_FRAME
12469   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12470   {
12471     SCAN_PLAYFIELD(x, y)
12472     {
12473       ChangeCount[x][y] = 0;
12474       ChangeEvent[x][y] = -1;
12475     }
12476   }
12477 #endif
12478
12479   if (game.set_centered_player)
12480   {
12481     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12482
12483     /* switching to "all players" only possible if all players fit to screen */
12484     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12485     {
12486       game.centered_player_nr_next = game.centered_player_nr;
12487       game.set_centered_player = FALSE;
12488     }
12489
12490     /* do not switch focus to non-existing (or non-active) player */
12491     if (game.centered_player_nr_next >= 0 &&
12492         !stored_player[game.centered_player_nr_next].active)
12493     {
12494       game.centered_player_nr_next = game.centered_player_nr;
12495       game.set_centered_player = FALSE;
12496     }
12497   }
12498
12499   if (game.set_centered_player &&
12500       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12501   {
12502     int sx, sy;
12503
12504     if (game.centered_player_nr_next == -1)
12505     {
12506       setScreenCenteredToAllPlayers(&sx, &sy);
12507     }
12508     else
12509     {
12510       sx = stored_player[game.centered_player_nr_next].jx;
12511       sy = stored_player[game.centered_player_nr_next].jy;
12512     }
12513
12514     game.centered_player_nr = game.centered_player_nr_next;
12515     game.set_centered_player = FALSE;
12516
12517     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12518     DrawGameDoorValues();
12519   }
12520
12521   for (i = 0; i < MAX_PLAYERS; i++)
12522   {
12523     int actual_player_action = stored_player[i].effective_action;
12524
12525 #if 1
12526     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12527        - rnd_equinox_tetrachloride 048
12528        - rnd_equinox_tetrachloride_ii 096
12529        - rnd_emanuel_schmieg 002
12530        - doctor_sloan_ww 001, 020
12531     */
12532     if (stored_player[i].MovPos == 0)
12533       CheckGravityMovement(&stored_player[i]);
12534 #endif
12535
12536     /* overwrite programmed action with tape action */
12537     if (stored_player[i].programmed_action)
12538       actual_player_action = stored_player[i].programmed_action;
12539
12540     PlayerActions(&stored_player[i], actual_player_action);
12541
12542     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12543   }
12544
12545   ScrollScreen(NULL, SCROLL_GO_ON);
12546
12547   /* for backwards compatibility, the following code emulates a fixed bug that
12548      occured when pushing elements (causing elements that just made their last
12549      pushing step to already (if possible) make their first falling step in the
12550      same game frame, which is bad); this code is also needed to use the famous
12551      "spring push bug" which is used in older levels and might be wanted to be
12552      used also in newer levels, but in this case the buggy pushing code is only
12553      affecting the "spring" element and no other elements */
12554
12555   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12556   {
12557     for (i = 0; i < MAX_PLAYERS; i++)
12558     {
12559       struct PlayerInfo *player = &stored_player[i];
12560       int x = player->jx;
12561       int y = player->jy;
12562
12563       if (player->active && player->is_pushing && player->is_moving &&
12564           IS_MOVING(x, y) &&
12565           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12566            Feld[x][y] == EL_SPRING))
12567       {
12568         ContinueMoving(x, y);
12569
12570         /* continue moving after pushing (this is actually a bug) */
12571         if (!IS_MOVING(x, y))
12572           Stop[x][y] = FALSE;
12573       }
12574     }
12575   }
12576
12577 #if 0
12578   debug_print_timestamp(0, "start main loop profiling");
12579 #endif
12580
12581   SCAN_PLAYFIELD(x, y)
12582   {
12583     ChangeCount[x][y] = 0;
12584     ChangeEvent[x][y] = -1;
12585
12586     /* this must be handled before main playfield loop */
12587     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12588     {
12589       MovDelay[x][y]--;
12590       if (MovDelay[x][y] <= 0)
12591         RemoveField(x, y);
12592     }
12593
12594 #if USE_NEW_SNAP_DELAY
12595     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12596     {
12597       MovDelay[x][y]--;
12598       if (MovDelay[x][y] <= 0)
12599       {
12600         RemoveField(x, y);
12601         TEST_DrawLevelField(x, y);
12602
12603         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12604       }
12605     }
12606 #endif
12607
12608 #if DEBUG
12609     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12610     {
12611       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12612       printf("GameActions(): This should never happen!\n");
12613
12614       ChangePage[x][y] = -1;
12615     }
12616 #endif
12617
12618     Stop[x][y] = FALSE;
12619     if (WasJustMoving[x][y] > 0)
12620       WasJustMoving[x][y]--;
12621     if (WasJustFalling[x][y] > 0)
12622       WasJustFalling[x][y]--;
12623     if (CheckCollision[x][y] > 0)
12624       CheckCollision[x][y]--;
12625     if (CheckImpact[x][y] > 0)
12626       CheckImpact[x][y]--;
12627
12628     GfxFrame[x][y]++;
12629
12630     /* reset finished pushing action (not done in ContinueMoving() to allow
12631        continuous pushing animation for elements with zero push delay) */
12632     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12633     {
12634       ResetGfxAnimation(x, y);
12635       TEST_DrawLevelField(x, y);
12636     }
12637
12638 #if DEBUG
12639     if (IS_BLOCKED(x, y))
12640     {
12641       int oldx, oldy;
12642
12643       Blocked2Moving(x, y, &oldx, &oldy);
12644       if (!IS_MOVING(oldx, oldy))
12645       {
12646         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12647         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12648         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12649         printf("GameActions(): This should never happen!\n");
12650       }
12651     }
12652 #endif
12653   }
12654
12655 #if 0
12656   debug_print_timestamp(0, "- time for pre-main loop:");
12657 #endif
12658
12659 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12660   SCAN_PLAYFIELD(x, y)
12661   {
12662     element = Feld[x][y];
12663     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12664
12665 #if 1
12666     {
12667 #if 1
12668       int element2 = element;
12669       int graphic2 = graphic;
12670 #else
12671       int element2 = Feld[x][y];
12672       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12673 #endif
12674       int last_gfx_frame = GfxFrame[x][y];
12675
12676       if (graphic_info[graphic2].anim_global_sync)
12677         GfxFrame[x][y] = FrameCounter;
12678       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12679         GfxFrame[x][y] = CustomValue[x][y];
12680       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12681         GfxFrame[x][y] = element_info[element2].collect_score;
12682       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12683         GfxFrame[x][y] = ChangeDelay[x][y];
12684
12685       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12686         DrawLevelGraphicAnimation(x, y, graphic2);
12687     }
12688 #else
12689     ResetGfxFrame(x, y, TRUE);
12690 #endif
12691
12692 #if 1
12693     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12694         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12695       ResetRandomAnimationValue(x, y);
12696 #endif
12697
12698 #if 1
12699     SetRandomAnimationValue(x, y);
12700 #endif
12701
12702 #if 1
12703     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12704 #endif
12705   }
12706 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12707
12708 #if 0
12709   debug_print_timestamp(0, "- time for TEST loop:     -->");
12710 #endif
12711
12712   SCAN_PLAYFIELD(x, y)
12713   {
12714     element = Feld[x][y];
12715     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12716
12717     ResetGfxFrame(x, y, TRUE);
12718
12719     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12720         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12721       ResetRandomAnimationValue(x, y);
12722
12723     SetRandomAnimationValue(x, y);
12724
12725     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12726
12727     if (IS_INACTIVE(element))
12728     {
12729       if (IS_ANIMATED(graphic))
12730         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12731
12732       continue;
12733     }
12734
12735     /* this may take place after moving, so 'element' may have changed */
12736     if (IS_CHANGING(x, y) &&
12737         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12738     {
12739       int page = element_info[element].event_page_nr[CE_DELAY];
12740
12741 #if 1
12742       HandleElementChange(x, y, page);
12743 #else
12744       if (CAN_CHANGE(element))
12745         HandleElementChange(x, y, page);
12746
12747       if (HAS_ACTION(element))
12748         ExecuteCustomElementAction(x, y, element, page);
12749 #endif
12750
12751       element = Feld[x][y];
12752       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12753     }
12754
12755 #if 0   // ---------------------------------------------------------------------
12756
12757     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12758     {
12759       StartMoving(x, y);
12760
12761       element = Feld[x][y];
12762       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12763
12764       if (IS_ANIMATED(graphic) &&
12765           !IS_MOVING(x, y) &&
12766           !Stop[x][y])
12767         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12768
12769       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12770         TEST_DrawTwinkleOnField(x, y);
12771     }
12772     else if (IS_MOVING(x, y))
12773       ContinueMoving(x, y);
12774     else
12775     {
12776       switch (element)
12777       {
12778         case EL_ACID:
12779         case EL_EXIT_OPEN:
12780         case EL_EM_EXIT_OPEN:
12781         case EL_SP_EXIT_OPEN:
12782         case EL_STEEL_EXIT_OPEN:
12783         case EL_EM_STEEL_EXIT_OPEN:
12784         case EL_SP_TERMINAL:
12785         case EL_SP_TERMINAL_ACTIVE:
12786         case EL_EXTRA_TIME:
12787         case EL_SHIELD_NORMAL:
12788         case EL_SHIELD_DEADLY:
12789           if (IS_ANIMATED(graphic))
12790             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12791           break;
12792
12793         case EL_DYNAMITE_ACTIVE:
12794         case EL_EM_DYNAMITE_ACTIVE:
12795         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12796         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12797         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12798         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12799         case EL_SP_DISK_RED_ACTIVE:
12800           CheckDynamite(x, y);
12801           break;
12802
12803         case EL_AMOEBA_GROWING:
12804           AmoebeWaechst(x, y);
12805           break;
12806
12807         case EL_AMOEBA_SHRINKING:
12808           AmoebaDisappearing(x, y);
12809           break;
12810
12811 #if !USE_NEW_AMOEBA_CODE
12812         case EL_AMOEBA_WET:
12813         case EL_AMOEBA_DRY:
12814         case EL_AMOEBA_FULL:
12815         case EL_BD_AMOEBA:
12816         case EL_EMC_DRIPPER:
12817           AmoebeAbleger(x, y);
12818           break;
12819 #endif
12820
12821         case EL_GAME_OF_LIFE:
12822         case EL_BIOMAZE:
12823           Life(x, y);
12824           break;
12825
12826         case EL_EXIT_CLOSED:
12827           CheckExit(x, y);
12828           break;
12829
12830         case EL_EM_EXIT_CLOSED:
12831           CheckExitEM(x, y);
12832           break;
12833
12834         case EL_STEEL_EXIT_CLOSED:
12835           CheckExitSteel(x, y);
12836           break;
12837
12838         case EL_EM_STEEL_EXIT_CLOSED:
12839           CheckExitSteelEM(x, y);
12840           break;
12841
12842         case EL_SP_EXIT_CLOSED:
12843           CheckExitSP(x, y);
12844           break;
12845
12846         case EL_EXPANDABLE_WALL_GROWING:
12847         case EL_EXPANDABLE_STEELWALL_GROWING:
12848           MauerWaechst(x, y);
12849           break;
12850
12851         case EL_EXPANDABLE_WALL:
12852         case EL_EXPANDABLE_WALL_HORIZONTAL:
12853         case EL_EXPANDABLE_WALL_VERTICAL:
12854         case EL_EXPANDABLE_WALL_ANY:
12855         case EL_BD_EXPANDABLE_WALL:
12856           MauerAbleger(x, y);
12857           break;
12858
12859         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12860         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12861         case EL_EXPANDABLE_STEELWALL_ANY:
12862           MauerAblegerStahl(x, y);
12863           break;
12864
12865         case EL_FLAMES:
12866           CheckForDragon(x, y);
12867           break;
12868
12869         case EL_EXPLOSION:
12870           break;
12871
12872         case EL_ELEMENT_SNAPPING:
12873         case EL_DIAGONAL_SHRINKING:
12874         case EL_DIAGONAL_GROWING:
12875         {
12876           graphic =
12877             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12878
12879           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12880           break;
12881         }
12882
12883         default:
12884           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12885             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12886           break;
12887       }
12888     }
12889
12890 #else   // ---------------------------------------------------------------------
12891
12892     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12893     {
12894       StartMoving(x, y);
12895
12896       element = Feld[x][y];
12897       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12898
12899       if (IS_ANIMATED(graphic) &&
12900           !IS_MOVING(x, y) &&
12901           !Stop[x][y])
12902         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12903
12904       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12905         TEST_DrawTwinkleOnField(x, y);
12906     }
12907     else if ((element == EL_ACID ||
12908               element == EL_EXIT_OPEN ||
12909               element == EL_EM_EXIT_OPEN ||
12910               element == EL_SP_EXIT_OPEN ||
12911               element == EL_STEEL_EXIT_OPEN ||
12912               element == EL_EM_STEEL_EXIT_OPEN ||
12913               element == EL_SP_TERMINAL ||
12914               element == EL_SP_TERMINAL_ACTIVE ||
12915               element == EL_EXTRA_TIME ||
12916               element == EL_SHIELD_NORMAL ||
12917               element == EL_SHIELD_DEADLY) &&
12918              IS_ANIMATED(graphic))
12919       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920     else if (IS_MOVING(x, y))
12921       ContinueMoving(x, y);
12922     else if (IS_ACTIVE_BOMB(element))
12923       CheckDynamite(x, y);
12924     else if (element == EL_AMOEBA_GROWING)
12925       AmoebeWaechst(x, y);
12926     else if (element == EL_AMOEBA_SHRINKING)
12927       AmoebaDisappearing(x, y);
12928
12929 #if !USE_NEW_AMOEBA_CODE
12930     else if (IS_AMOEBALIVE(element))
12931       AmoebeAbleger(x, y);
12932 #endif
12933
12934     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12935       Life(x, y);
12936     else if (element == EL_EXIT_CLOSED)
12937       CheckExit(x, y);
12938     else if (element == EL_EM_EXIT_CLOSED)
12939       CheckExitEM(x, y);
12940     else if (element == EL_STEEL_EXIT_CLOSED)
12941       CheckExitSteel(x, y);
12942     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12943       CheckExitSteelEM(x, y);
12944     else if (element == EL_SP_EXIT_CLOSED)
12945       CheckExitSP(x, y);
12946     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12947              element == EL_EXPANDABLE_STEELWALL_GROWING)
12948       MauerWaechst(x, y);
12949     else if (element == EL_EXPANDABLE_WALL ||
12950              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12951              element == EL_EXPANDABLE_WALL_VERTICAL ||
12952              element == EL_EXPANDABLE_WALL_ANY ||
12953              element == EL_BD_EXPANDABLE_WALL)
12954       MauerAbleger(x, y);
12955     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12956              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12957              element == EL_EXPANDABLE_STEELWALL_ANY)
12958       MauerAblegerStahl(x, y);
12959     else if (element == EL_FLAMES)
12960       CheckForDragon(x, y);
12961     else if (element == EL_EXPLOSION)
12962       ; /* drawing of correct explosion animation is handled separately */
12963     else if (element == EL_ELEMENT_SNAPPING ||
12964              element == EL_DIAGONAL_SHRINKING ||
12965              element == EL_DIAGONAL_GROWING)
12966     {
12967       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12968
12969       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12970     }
12971     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12972       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12973
12974 #endif  // ---------------------------------------------------------------------
12975
12976     if (IS_BELT_ACTIVE(element))
12977       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12978
12979     if (game.magic_wall_active)
12980     {
12981       int jx = local_player->jx, jy = local_player->jy;
12982
12983       /* play the element sound at the position nearest to the player */
12984       if ((element == EL_MAGIC_WALL_FULL ||
12985            element == EL_MAGIC_WALL_ACTIVE ||
12986            element == EL_MAGIC_WALL_EMPTYING ||
12987            element == EL_BD_MAGIC_WALL_FULL ||
12988            element == EL_BD_MAGIC_WALL_ACTIVE ||
12989            element == EL_BD_MAGIC_WALL_EMPTYING ||
12990            element == EL_DC_MAGIC_WALL_FULL ||
12991            element == EL_DC_MAGIC_WALL_ACTIVE ||
12992            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12993           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12994       {
12995         magic_wall_x = x;
12996         magic_wall_y = y;
12997       }
12998     }
12999   }
13000
13001 #if 0
13002   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13003 #endif
13004
13005 #if USE_NEW_AMOEBA_CODE
13006   /* new experimental amoeba growth stuff */
13007   if (!(FrameCounter % 8))
13008   {
13009     static unsigned long random = 1684108901;
13010
13011     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13012     {
13013       x = RND(lev_fieldx);
13014       y = RND(lev_fieldy);
13015       element = Feld[x][y];
13016
13017       if (!IS_PLAYER(x,y) &&
13018           (element == EL_EMPTY ||
13019            CAN_GROW_INTO(element) ||
13020            element == EL_QUICKSAND_EMPTY ||
13021            element == EL_QUICKSAND_FAST_EMPTY ||
13022            element == EL_ACID_SPLASH_LEFT ||
13023            element == EL_ACID_SPLASH_RIGHT))
13024       {
13025         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13026             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13027             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13028             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13029           Feld[x][y] = EL_AMOEBA_DROP;
13030       }
13031
13032       random = random * 129 + 1;
13033     }
13034   }
13035 #endif
13036
13037 #if 0
13038   if (game.explosions_delayed)
13039 #endif
13040   {
13041     game.explosions_delayed = FALSE;
13042
13043     SCAN_PLAYFIELD(x, y)
13044     {
13045       element = Feld[x][y];
13046
13047       if (ExplodeField[x][y])
13048         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13049       else if (element == EL_EXPLOSION)
13050         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13051
13052       ExplodeField[x][y] = EX_TYPE_NONE;
13053     }
13054
13055     game.explosions_delayed = TRUE;
13056   }
13057
13058   if (game.magic_wall_active)
13059   {
13060     if (!(game.magic_wall_time_left % 4))
13061     {
13062       int element = Feld[magic_wall_x][magic_wall_y];
13063
13064       if (element == EL_BD_MAGIC_WALL_FULL ||
13065           element == EL_BD_MAGIC_WALL_ACTIVE ||
13066           element == EL_BD_MAGIC_WALL_EMPTYING)
13067         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13068       else if (element == EL_DC_MAGIC_WALL_FULL ||
13069                element == EL_DC_MAGIC_WALL_ACTIVE ||
13070                element == EL_DC_MAGIC_WALL_EMPTYING)
13071         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13072       else
13073         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13074     }
13075
13076     if (game.magic_wall_time_left > 0)
13077     {
13078       game.magic_wall_time_left--;
13079
13080       if (!game.magic_wall_time_left)
13081       {
13082         SCAN_PLAYFIELD(x, y)
13083         {
13084           element = Feld[x][y];
13085
13086           if (element == EL_MAGIC_WALL_ACTIVE ||
13087               element == EL_MAGIC_WALL_FULL)
13088           {
13089             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13090             TEST_DrawLevelField(x, y);
13091           }
13092           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13093                    element == EL_BD_MAGIC_WALL_FULL)
13094           {
13095             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13096             TEST_DrawLevelField(x, y);
13097           }
13098           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13099                    element == EL_DC_MAGIC_WALL_FULL)
13100           {
13101             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13102             TEST_DrawLevelField(x, y);
13103           }
13104         }
13105
13106         game.magic_wall_active = FALSE;
13107       }
13108     }
13109   }
13110
13111   if (game.light_time_left > 0)
13112   {
13113     game.light_time_left--;
13114
13115     if (game.light_time_left == 0)
13116       RedrawAllLightSwitchesAndInvisibleElements();
13117   }
13118
13119   if (game.timegate_time_left > 0)
13120   {
13121     game.timegate_time_left--;
13122
13123     if (game.timegate_time_left == 0)
13124       CloseAllOpenTimegates();
13125   }
13126
13127   if (game.lenses_time_left > 0)
13128   {
13129     game.lenses_time_left--;
13130
13131     if (game.lenses_time_left == 0)
13132       RedrawAllInvisibleElementsForLenses();
13133   }
13134
13135   if (game.magnify_time_left > 0)
13136   {
13137     game.magnify_time_left--;
13138
13139     if (game.magnify_time_left == 0)
13140       RedrawAllInvisibleElementsForMagnifier();
13141   }
13142
13143   for (i = 0; i < MAX_PLAYERS; i++)
13144   {
13145     struct PlayerInfo *player = &stored_player[i];
13146
13147     if (SHIELD_ON(player))
13148     {
13149       if (player->shield_deadly_time_left)
13150         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13151       else if (player->shield_normal_time_left)
13152         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13153     }
13154   }
13155
13156 #if USE_DELAYED_GFX_REDRAW
13157   SCAN_PLAYFIELD(x, y)
13158   {
13159 #if 1
13160     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13161 #else
13162     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13163         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13164 #endif
13165     {
13166       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13167          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13168
13169       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13170         DrawLevelField(x, y);
13171
13172       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13173         DrawLevelFieldCrumbledSand(x, y);
13174
13175       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13176         DrawLevelFieldCrumbledSandNeighbours(x, y);
13177
13178       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13179         DrawTwinkleOnField(x, y);
13180     }
13181
13182     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13183   }
13184 #endif
13185
13186   CheckLevelTime();
13187
13188   DrawAllPlayers();
13189   PlayAllPlayersSound();
13190
13191   if (options.debug)                    /* calculate frames per second */
13192   {
13193     static unsigned long fps_counter = 0;
13194     static int fps_frames = 0;
13195     unsigned long fps_delay_ms = Counter() - fps_counter;
13196
13197     fps_frames++;
13198
13199     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13200     {
13201       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13202
13203       fps_frames = 0;
13204       fps_counter = Counter();
13205     }
13206
13207     redraw_mask |= REDRAW_FPS;
13208   }
13209
13210   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13211
13212   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13213   {
13214     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13215
13216     local_player->show_envelope = 0;
13217   }
13218
13219 #if 0
13220   debug_print_timestamp(0, "stop main loop profiling ");
13221   printf("----------------------------------------------------------\n");
13222 #endif
13223
13224   /* use random number generator in every frame to make it less predictable */
13225   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13226     RND(1);
13227 }
13228
13229 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13230 {
13231   int min_x = x, min_y = y, max_x = x, max_y = y;
13232   int i;
13233
13234   for (i = 0; i < MAX_PLAYERS; i++)
13235   {
13236     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13237
13238     if (!stored_player[i].active || &stored_player[i] == player)
13239       continue;
13240
13241     min_x = MIN(min_x, jx);
13242     min_y = MIN(min_y, jy);
13243     max_x = MAX(max_x, jx);
13244     max_y = MAX(max_y, jy);
13245   }
13246
13247   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13248 }
13249
13250 static boolean AllPlayersInVisibleScreen()
13251 {
13252   int i;
13253
13254   for (i = 0; i < MAX_PLAYERS; i++)
13255   {
13256     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13257
13258     if (!stored_player[i].active)
13259       continue;
13260
13261     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13262       return FALSE;
13263   }
13264
13265   return TRUE;
13266 }
13267
13268 void ScrollLevel(int dx, int dy)
13269 {
13270 #if 0
13271   /* (directly solved in BlitBitmap() now) */
13272   static Bitmap *bitmap_db_field2 = NULL;
13273   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13274   int x, y;
13275 #else
13276   int x, y;
13277 #endif
13278
13279 #if 0
13280   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13281   /* only horizontal XOR vertical scroll direction allowed */
13282   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13283     return;
13284 #endif
13285
13286 #if 0
13287   /* (directly solved in BlitBitmap() now) */
13288   if (bitmap_db_field2 == NULL)
13289     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13290
13291   /* needed when blitting directly to same bitmap -- should not be needed with
13292      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13293   BlitBitmap(drawto_field, bitmap_db_field2,
13294              FX + TILEX * (dx == -1) - softscroll_offset,
13295              FY + TILEY * (dy == -1) - softscroll_offset,
13296              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13297              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13298              FX + TILEX * (dx == 1) - softscroll_offset,
13299              FY + TILEY * (dy == 1) - softscroll_offset);
13300   BlitBitmap(bitmap_db_field2, drawto_field,
13301              FX + TILEX * (dx == 1) - softscroll_offset,
13302              FY + TILEY * (dy == 1) - softscroll_offset,
13303              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13304              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13305              FX + TILEX * (dx == 1) - softscroll_offset,
13306              FY + TILEY * (dy == 1) - softscroll_offset);
13307
13308 #else
13309
13310 #if 0
13311   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13312   int xsize = (BX2 - BX1 + 1);
13313   int ysize = (BY2 - BY1 + 1);
13314   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13315   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13316   int step  = (start < end ? +1 : -1);
13317
13318   for (i = start; i != end; i += step)
13319   {
13320     BlitBitmap(drawto_field, drawto_field,
13321                FX + TILEX * (dx != 0 ? i + step : 0),
13322                FY + TILEY * (dy != 0 ? i + step : 0),
13323                TILEX * (dx != 0 ? 1 : xsize),
13324                TILEY * (dy != 0 ? 1 : ysize),
13325                FX + TILEX * (dx != 0 ? i : 0),
13326                FY + TILEY * (dy != 0 ? i : 0));
13327   }
13328
13329 #else
13330
13331   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13332
13333   BlitBitmap(drawto_field, drawto_field,
13334              FX + TILEX * (dx == -1) - softscroll_offset,
13335              FY + TILEY * (dy == -1) - softscroll_offset,
13336              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13337              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13338              FX + TILEX * (dx == 1) - softscroll_offset,
13339              FY + TILEY * (dy == 1) - softscroll_offset);
13340 #endif
13341 #endif
13342
13343   if (dx != 0)
13344   {
13345     x = (dx == 1 ? BX1 : BX2);
13346     for (y = BY1; y <= BY2; y++)
13347       DrawScreenField(x, y);
13348   }
13349
13350   if (dy != 0)
13351   {
13352     y = (dy == 1 ? BY1 : BY2);
13353     for (x = BX1; x <= BX2; x++)
13354       DrawScreenField(x, y);
13355   }
13356
13357   redraw_mask |= REDRAW_FIELD;
13358 }
13359
13360 static boolean canFallDown(struct PlayerInfo *player)
13361 {
13362   int jx = player->jx, jy = player->jy;
13363
13364   return (IN_LEV_FIELD(jx, jy + 1) &&
13365           (IS_FREE(jx, jy + 1) ||
13366            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13367           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13368           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13369 }
13370
13371 static boolean canPassField(int x, int y, int move_dir)
13372 {
13373   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13374   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13375   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13376   int nextx = x + dx;
13377   int nexty = y + dy;
13378   int element = Feld[x][y];
13379
13380   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13381           !CAN_MOVE(element) &&
13382           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13383           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13384           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13385 }
13386
13387 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13388 {
13389   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13390   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13391   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13392   int newx = x + dx;
13393   int newy = y + dy;
13394
13395   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13396           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13397           (IS_DIGGABLE(Feld[newx][newy]) ||
13398            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13399            canPassField(newx, newy, move_dir)));
13400 }
13401
13402 static void CheckGravityMovement(struct PlayerInfo *player)
13403 {
13404 #if USE_PLAYER_GRAVITY
13405   if (player->gravity && !player->programmed_action)
13406 #else
13407   if (game.gravity && !player->programmed_action)
13408 #endif
13409   {
13410     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13411     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13412     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13413     int jx = player->jx, jy = player->jy;
13414     boolean player_is_moving_to_valid_field =
13415       (!player_is_snapping &&
13416        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13417         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13418     boolean player_can_fall_down = canFallDown(player);
13419
13420     if (player_can_fall_down &&
13421         !player_is_moving_to_valid_field)
13422       player->programmed_action = MV_DOWN;
13423   }
13424 }
13425
13426 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13427 {
13428   return CheckGravityMovement(player);
13429
13430 #if USE_PLAYER_GRAVITY
13431   if (player->gravity && !player->programmed_action)
13432 #else
13433   if (game.gravity && !player->programmed_action)
13434 #endif
13435   {
13436     int jx = player->jx, jy = player->jy;
13437     boolean field_under_player_is_free =
13438       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13439     boolean player_is_standing_on_valid_field =
13440       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13441        (IS_WALKABLE(Feld[jx][jy]) &&
13442         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13443
13444     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13445       player->programmed_action = MV_DOWN;
13446   }
13447 }
13448
13449 /*
13450   MovePlayerOneStep()
13451   -----------------------------------------------------------------------------
13452   dx, dy:               direction (non-diagonal) to try to move the player to
13453   real_dx, real_dy:     direction as read from input device (can be diagonal)
13454 */
13455
13456 boolean MovePlayerOneStep(struct PlayerInfo *player,
13457                           int dx, int dy, int real_dx, int real_dy)
13458 {
13459   int jx = player->jx, jy = player->jy;
13460   int new_jx = jx + dx, new_jy = jy + dy;
13461 #if !USE_FIXED_DONT_RUN_INTO
13462   int element;
13463 #endif
13464   int can_move;
13465   boolean player_can_move = !player->cannot_move;
13466
13467   if (!player->active || (!dx && !dy))
13468     return MP_NO_ACTION;
13469
13470   player->MovDir = (dx < 0 ? MV_LEFT :
13471                     dx > 0 ? MV_RIGHT :
13472                     dy < 0 ? MV_UP :
13473                     dy > 0 ? MV_DOWN :  MV_NONE);
13474
13475   if (!IN_LEV_FIELD(new_jx, new_jy))
13476     return MP_NO_ACTION;
13477
13478   if (!player_can_move)
13479   {
13480     if (player->MovPos == 0)
13481     {
13482       player->is_moving = FALSE;
13483       player->is_digging = FALSE;
13484       player->is_collecting = FALSE;
13485       player->is_snapping = FALSE;
13486       player->is_pushing = FALSE;
13487     }
13488   }
13489
13490 #if 1
13491   if (!options.network && game.centered_player_nr == -1 &&
13492       !AllPlayersInSight(player, new_jx, new_jy))
13493     return MP_NO_ACTION;
13494 #else
13495   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13496     return MP_NO_ACTION;
13497 #endif
13498
13499 #if !USE_FIXED_DONT_RUN_INTO
13500   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13501
13502   /* (moved to DigField()) */
13503   if (player_can_move && DONT_RUN_INTO(element))
13504   {
13505     if (element == EL_ACID && dx == 0 && dy == 1)
13506     {
13507       SplashAcid(new_jx, new_jy);
13508       Feld[jx][jy] = EL_PLAYER_1;
13509       InitMovingField(jx, jy, MV_DOWN);
13510       Store[jx][jy] = EL_ACID;
13511       ContinueMoving(jx, jy);
13512       BuryPlayer(player);
13513     }
13514     else
13515       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13516
13517     return MP_MOVING;
13518   }
13519 #endif
13520
13521   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13522   if (can_move != MP_MOVING)
13523     return can_move;
13524
13525   /* check if DigField() has caused relocation of the player */
13526   if (player->jx != jx || player->jy != jy)
13527     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13528
13529   StorePlayer[jx][jy] = 0;
13530   player->last_jx = jx;
13531   player->last_jy = jy;
13532   player->jx = new_jx;
13533   player->jy = new_jy;
13534   StorePlayer[new_jx][new_jy] = player->element_nr;
13535
13536   if (player->move_delay_value_next != -1)
13537   {
13538     player->move_delay_value = player->move_delay_value_next;
13539     player->move_delay_value_next = -1;
13540   }
13541
13542   player->MovPos =
13543     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13544
13545   player->step_counter++;
13546
13547   PlayerVisit[jx][jy] = FrameCounter;
13548
13549 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13550   player->is_moving = TRUE;
13551 #endif
13552
13553 #if 1
13554   /* should better be called in MovePlayer(), but this breaks some tapes */
13555   ScrollPlayer(player, SCROLL_INIT);
13556 #endif
13557
13558   return MP_MOVING;
13559 }
13560
13561 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13562 {
13563   int jx = player->jx, jy = player->jy;
13564   int old_jx = jx, old_jy = jy;
13565   int moved = MP_NO_ACTION;
13566
13567   if (!player->active)
13568     return FALSE;
13569
13570   if (!dx && !dy)
13571   {
13572     if (player->MovPos == 0)
13573     {
13574       player->is_moving = FALSE;
13575       player->is_digging = FALSE;
13576       player->is_collecting = FALSE;
13577       player->is_snapping = FALSE;
13578       player->is_pushing = FALSE;
13579     }
13580
13581     return FALSE;
13582   }
13583
13584   if (player->move_delay > 0)
13585     return FALSE;
13586
13587   player->move_delay = -1;              /* set to "uninitialized" value */
13588
13589   /* store if player is automatically moved to next field */
13590   player->is_auto_moving = (player->programmed_action != MV_NONE);
13591
13592   /* remove the last programmed player action */
13593   player->programmed_action = 0;
13594
13595   if (player->MovPos)
13596   {
13597     /* should only happen if pre-1.2 tape recordings are played */
13598     /* this is only for backward compatibility */
13599
13600     int original_move_delay_value = player->move_delay_value;
13601
13602 #if DEBUG
13603     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13604            tape.counter);
13605 #endif
13606
13607     /* scroll remaining steps with finest movement resolution */
13608     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13609
13610     while (player->MovPos)
13611     {
13612       ScrollPlayer(player, SCROLL_GO_ON);
13613       ScrollScreen(NULL, SCROLL_GO_ON);
13614
13615       AdvanceFrameAndPlayerCounters(player->index_nr);
13616
13617       DrawAllPlayers();
13618       BackToFront();
13619     }
13620
13621     player->move_delay_value = original_move_delay_value;
13622   }
13623
13624   player->is_active = FALSE;
13625
13626   if (player->last_move_dir & MV_HORIZONTAL)
13627   {
13628     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13629       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13630   }
13631   else
13632   {
13633     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13634       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13635   }
13636
13637 #if USE_FIXED_BORDER_RUNNING_GFX
13638   if (!moved && !player->is_active)
13639   {
13640     player->is_moving = FALSE;
13641     player->is_digging = FALSE;
13642     player->is_collecting = FALSE;
13643     player->is_snapping = FALSE;
13644     player->is_pushing = FALSE;
13645   }
13646 #endif
13647
13648   jx = player->jx;
13649   jy = player->jy;
13650
13651 #if 1
13652   if (moved & MP_MOVING && !ScreenMovPos &&
13653       (player->index_nr == game.centered_player_nr ||
13654        game.centered_player_nr == -1))
13655 #else
13656   if (moved & MP_MOVING && !ScreenMovPos &&
13657       (player == local_player || !options.network))
13658 #endif
13659   {
13660     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13661     int offset = game.scroll_delay_value;
13662
13663     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13664     {
13665       /* actual player has left the screen -- scroll in that direction */
13666       if (jx != old_jx)         /* player has moved horizontally */
13667         scroll_x += (jx - old_jx);
13668       else                      /* player has moved vertically */
13669         scroll_y += (jy - old_jy);
13670     }
13671     else
13672     {
13673       if (jx != old_jx)         /* player has moved horizontally */
13674       {
13675         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13676             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13677           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13678
13679         /* don't scroll over playfield boundaries */
13680         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13681           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13682
13683         /* don't scroll more than one field at a time */
13684         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13685
13686         /* don't scroll against the player's moving direction */
13687         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13688             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13689           scroll_x = old_scroll_x;
13690       }
13691       else                      /* player has moved vertically */
13692       {
13693         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13694             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13695           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13696
13697         /* don't scroll over playfield boundaries */
13698         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13699           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13700
13701         /* don't scroll more than one field at a time */
13702         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13703
13704         /* don't scroll against the player's moving direction */
13705         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13706             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13707           scroll_y = old_scroll_y;
13708       }
13709     }
13710
13711     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13712     {
13713 #if 1
13714       if (!options.network && game.centered_player_nr == -1 &&
13715           !AllPlayersInVisibleScreen())
13716       {
13717         scroll_x = old_scroll_x;
13718         scroll_y = old_scroll_y;
13719       }
13720       else
13721 #else
13722       if (!options.network && !AllPlayersInVisibleScreen())
13723       {
13724         scroll_x = old_scroll_x;
13725         scroll_y = old_scroll_y;
13726       }
13727       else
13728 #endif
13729       {
13730         ScrollScreen(player, SCROLL_INIT);
13731         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13732       }
13733     }
13734   }
13735
13736   player->StepFrame = 0;
13737
13738   if (moved & MP_MOVING)
13739   {
13740     if (old_jx != jx && old_jy == jy)
13741       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13742     else if (old_jx == jx && old_jy != jy)
13743       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13744
13745     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13746
13747     player->last_move_dir = player->MovDir;
13748     player->is_moving = TRUE;
13749     player->is_snapping = FALSE;
13750     player->is_switching = FALSE;
13751     player->is_dropping = FALSE;
13752     player->is_dropping_pressed = FALSE;
13753     player->drop_pressed_delay = 0;
13754
13755 #if 0
13756     /* should better be called here than above, but this breaks some tapes */
13757     ScrollPlayer(player, SCROLL_INIT);
13758 #endif
13759   }
13760   else
13761   {
13762     CheckGravityMovementWhenNotMoving(player);
13763
13764     player->is_moving = FALSE;
13765
13766     /* at this point, the player is allowed to move, but cannot move right now
13767        (e.g. because of something blocking the way) -- ensure that the player
13768        is also allowed to move in the next frame (in old versions before 3.1.1,
13769        the player was forced to wait again for eight frames before next try) */
13770
13771     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13772       player->move_delay = 0;   /* allow direct movement in the next frame */
13773   }
13774
13775   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13776     player->move_delay = player->move_delay_value;
13777
13778   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13779   {
13780     TestIfPlayerTouchesBadThing(jx, jy);
13781     TestIfPlayerTouchesCustomElement(jx, jy);
13782   }
13783
13784   if (!player->active)
13785     RemovePlayer(player);
13786
13787   return moved;
13788 }
13789
13790 void ScrollPlayer(struct PlayerInfo *player, int mode)
13791 {
13792   int jx = player->jx, jy = player->jy;
13793   int last_jx = player->last_jx, last_jy = player->last_jy;
13794   int move_stepsize = TILEX / player->move_delay_value;
13795
13796 #if USE_NEW_PLAYER_SPEED
13797   if (!player->active)
13798     return;
13799
13800   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13801     return;
13802 #else
13803   if (!player->active || player->MovPos == 0)
13804     return;
13805 #endif
13806
13807   if (mode == SCROLL_INIT)
13808   {
13809     player->actual_frame_counter = FrameCounter;
13810     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13811
13812     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13813         Feld[last_jx][last_jy] == EL_EMPTY)
13814     {
13815       int last_field_block_delay = 0;   /* start with no blocking at all */
13816       int block_delay_adjustment = player->block_delay_adjustment;
13817
13818       /* if player blocks last field, add delay for exactly one move */
13819       if (player->block_last_field)
13820       {
13821         last_field_block_delay += player->move_delay_value;
13822
13823         /* when blocking enabled, prevent moving up despite gravity */
13824 #if USE_PLAYER_GRAVITY
13825         if (player->gravity && player->MovDir == MV_UP)
13826           block_delay_adjustment = -1;
13827 #else
13828         if (game.gravity && player->MovDir == MV_UP)
13829           block_delay_adjustment = -1;
13830 #endif
13831       }
13832
13833       /* add block delay adjustment (also possible when not blocking) */
13834       last_field_block_delay += block_delay_adjustment;
13835
13836       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13837       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13838     }
13839
13840 #if USE_NEW_PLAYER_SPEED
13841     if (player->MovPos != 0)    /* player has not yet reached destination */
13842       return;
13843 #else
13844     return;
13845 #endif
13846   }
13847   else if (!FrameReached(&player->actual_frame_counter, 1))
13848     return;
13849
13850 #if USE_NEW_PLAYER_SPEED
13851   if (player->MovPos != 0)
13852   {
13853     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13854     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13855
13856     /* before DrawPlayer() to draw correct player graphic for this case */
13857     if (player->MovPos == 0)
13858       CheckGravityMovement(player);
13859   }
13860 #else
13861   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13862   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13863
13864   /* before DrawPlayer() to draw correct player graphic for this case */
13865   if (player->MovPos == 0)
13866     CheckGravityMovement(player);
13867 #endif
13868
13869   if (player->MovPos == 0)      /* player reached destination field */
13870   {
13871     if (player->move_delay_reset_counter > 0)
13872     {
13873       player->move_delay_reset_counter--;
13874
13875       if (player->move_delay_reset_counter == 0)
13876       {
13877         /* continue with normal speed after quickly moving through gate */
13878         HALVE_PLAYER_SPEED(player);
13879
13880         /* be able to make the next move without delay */
13881         player->move_delay = 0;
13882       }
13883     }
13884
13885     player->last_jx = jx;
13886     player->last_jy = jy;
13887
13888     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13889         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13890 #if 1
13891         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13892 #endif
13893         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13894         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13895 #if 1
13896         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13897 #endif
13898         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13899         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13900     {
13901       DrawPlayer(player);       /* needed here only to cleanup last field */
13902       RemovePlayer(player);
13903
13904       if (local_player->friends_still_needed == 0 ||
13905           IS_SP_ELEMENT(Feld[jx][jy]))
13906         PlayerWins(player);
13907     }
13908
13909     /* this breaks one level: "machine", level 000 */
13910     {
13911       int move_direction = player->MovDir;
13912       int enter_side = MV_DIR_OPPOSITE(move_direction);
13913       int leave_side = move_direction;
13914       int old_jx = last_jx;
13915       int old_jy = last_jy;
13916       int old_element = Feld[old_jx][old_jy];
13917       int new_element = Feld[jx][jy];
13918
13919       if (IS_CUSTOM_ELEMENT(old_element))
13920         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13921                                    CE_LEFT_BY_PLAYER,
13922                                    player->index_bit, leave_side);
13923
13924       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13925                                           CE_PLAYER_LEAVES_X,
13926                                           player->index_bit, leave_side);
13927
13928       if (IS_CUSTOM_ELEMENT(new_element))
13929         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13930                                    player->index_bit, enter_side);
13931
13932       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13933                                           CE_PLAYER_ENTERS_X,
13934                                           player->index_bit, enter_side);
13935
13936 #if USE_FIX_CE_ACTION_WITH_PLAYER
13937       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13938                                         CE_MOVE_OF_X, move_direction);
13939 #else
13940       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13941                                         CE_MOVE_OF_X, move_direction);
13942 #endif
13943     }
13944
13945     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13946     {
13947       TestIfPlayerTouchesBadThing(jx, jy);
13948       TestIfPlayerTouchesCustomElement(jx, jy);
13949
13950       /* needed because pushed element has not yet reached its destination,
13951          so it would trigger a change event at its previous field location */
13952       if (!player->is_pushing)
13953         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13954
13955       if (!player->active)
13956         RemovePlayer(player);
13957     }
13958
13959     if (!local_player->LevelSolved && level.use_step_counter)
13960     {
13961       int i;
13962
13963       TimePlayed++;
13964
13965       if (TimeLeft > 0)
13966       {
13967         TimeLeft--;
13968
13969         if (TimeLeft <= 10 && setup.time_limit)
13970           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13971
13972 #if 1
13973         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13974
13975         DisplayGameControlValues();
13976 #else
13977         DrawGameValue_Time(TimeLeft);
13978 #endif
13979
13980         if (!TimeLeft && setup.time_limit)
13981           for (i = 0; i < MAX_PLAYERS; i++)
13982             KillPlayer(&stored_player[i]);
13983       }
13984 #if 1
13985       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13986       {
13987         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13988
13989         DisplayGameControlValues();
13990       }
13991 #else
13992       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13993         DrawGameValue_Time(TimePlayed);
13994 #endif
13995     }
13996
13997     if (tape.single_step && tape.recording && !tape.pausing &&
13998         !player->programmed_action)
13999       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14000   }
14001 }
14002
14003 void ScrollScreen(struct PlayerInfo *player, int mode)
14004 {
14005   static unsigned long screen_frame_counter = 0;
14006
14007   if (mode == SCROLL_INIT)
14008   {
14009     /* set scrolling step size according to actual player's moving speed */
14010     ScrollStepSize = TILEX / player->move_delay_value;
14011
14012     screen_frame_counter = FrameCounter;
14013     ScreenMovDir = player->MovDir;
14014     ScreenMovPos = player->MovPos;
14015     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14016     return;
14017   }
14018   else if (!FrameReached(&screen_frame_counter, 1))
14019     return;
14020
14021   if (ScreenMovPos)
14022   {
14023     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14024     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14025     redraw_mask |= REDRAW_FIELD;
14026   }
14027   else
14028     ScreenMovDir = MV_NONE;
14029 }
14030
14031 void TestIfPlayerTouchesCustomElement(int x, int y)
14032 {
14033   static int xy[4][2] =
14034   {
14035     { 0, -1 },
14036     { -1, 0 },
14037     { +1, 0 },
14038     { 0, +1 }
14039   };
14040   static int trigger_sides[4][2] =
14041   {
14042     /* center side       border side */
14043     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14044     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14045     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14046     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14047   };
14048   static int touch_dir[4] =
14049   {
14050     MV_LEFT | MV_RIGHT,
14051     MV_UP   | MV_DOWN,
14052     MV_UP   | MV_DOWN,
14053     MV_LEFT | MV_RIGHT
14054   };
14055   int center_element = Feld[x][y];      /* should always be non-moving! */
14056   int i;
14057
14058   for (i = 0; i < NUM_DIRECTIONS; i++)
14059   {
14060     int xx = x + xy[i][0];
14061     int yy = y + xy[i][1];
14062     int center_side = trigger_sides[i][0];
14063     int border_side = trigger_sides[i][1];
14064     int border_element;
14065
14066     if (!IN_LEV_FIELD(xx, yy))
14067       continue;
14068
14069     if (IS_PLAYER(x, y))                /* player found at center element */
14070     {
14071       struct PlayerInfo *player = PLAYERINFO(x, y);
14072
14073       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14074         border_element = Feld[xx][yy];          /* may be moving! */
14075       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14076         border_element = Feld[xx][yy];
14077       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14078         border_element = MovingOrBlocked2Element(xx, yy);
14079       else
14080         continue;               /* center and border element do not touch */
14081
14082       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14083                                  player->index_bit, border_side);
14084       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14085                                           CE_PLAYER_TOUCHES_X,
14086                                           player->index_bit, border_side);
14087
14088 #if USE_FIX_CE_ACTION_WITH_PLAYER
14089       {
14090         /* use player element that is initially defined in the level playfield,
14091            not the player element that corresponds to the runtime player number
14092            (example: a level that contains EL_PLAYER_3 as the only player would
14093            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14094         int player_element = PLAYERINFO(x, y)->initial_element;
14095
14096         CheckElementChangeBySide(xx, yy, border_element, player_element,
14097                                  CE_TOUCHING_X, border_side);
14098       }
14099 #endif
14100     }
14101     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14102     {
14103       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14104
14105       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14106       {
14107         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14108           continue;             /* center and border element do not touch */
14109       }
14110
14111       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14112                                  player->index_bit, center_side);
14113       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14114                                           CE_PLAYER_TOUCHES_X,
14115                                           player->index_bit, center_side);
14116
14117 #if USE_FIX_CE_ACTION_WITH_PLAYER
14118       {
14119         /* use player element that is initially defined in the level playfield,
14120            not the player element that corresponds to the runtime player number
14121            (example: a level that contains EL_PLAYER_3 as the only player would
14122            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14123         int player_element = PLAYERINFO(xx, yy)->initial_element;
14124
14125         CheckElementChangeBySide(x, y, center_element, player_element,
14126                                  CE_TOUCHING_X, center_side);
14127       }
14128 #endif
14129
14130       break;
14131     }
14132   }
14133 }
14134
14135 #if USE_ELEMENT_TOUCHING_BUGFIX
14136
14137 void TestIfElementTouchesCustomElement(int x, int y)
14138 {
14139   static int xy[4][2] =
14140   {
14141     { 0, -1 },
14142     { -1, 0 },
14143     { +1, 0 },
14144     { 0, +1 }
14145   };
14146   static int trigger_sides[4][2] =
14147   {
14148     /* center side      border side */
14149     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14150     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14151     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14152     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14153   };
14154   static int touch_dir[4] =
14155   {
14156     MV_LEFT | MV_RIGHT,
14157     MV_UP   | MV_DOWN,
14158     MV_UP   | MV_DOWN,
14159     MV_LEFT | MV_RIGHT
14160   };
14161   boolean change_center_element = FALSE;
14162   int center_element = Feld[x][y];      /* should always be non-moving! */
14163   int border_element_old[NUM_DIRECTIONS];
14164   int i;
14165
14166   for (i = 0; i < NUM_DIRECTIONS; i++)
14167   {
14168     int xx = x + xy[i][0];
14169     int yy = y + xy[i][1];
14170     int border_element;
14171
14172     border_element_old[i] = -1;
14173
14174     if (!IN_LEV_FIELD(xx, yy))
14175       continue;
14176
14177     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14178       border_element = Feld[xx][yy];    /* may be moving! */
14179     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14180       border_element = Feld[xx][yy];
14181     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14182       border_element = MovingOrBlocked2Element(xx, yy);
14183     else
14184       continue;                 /* center and border element do not touch */
14185
14186     border_element_old[i] = border_element;
14187   }
14188
14189   for (i = 0; i < NUM_DIRECTIONS; i++)
14190   {
14191     int xx = x + xy[i][0];
14192     int yy = y + xy[i][1];
14193     int center_side = trigger_sides[i][0];
14194     int border_element = border_element_old[i];
14195
14196     if (border_element == -1)
14197       continue;
14198
14199     /* check for change of border element */
14200     CheckElementChangeBySide(xx, yy, border_element, center_element,
14201                              CE_TOUCHING_X, center_side);
14202
14203     /* (center element cannot be player, so we dont have to check this here) */
14204   }
14205
14206   for (i = 0; i < NUM_DIRECTIONS; i++)
14207   {
14208     int xx = x + xy[i][0];
14209     int yy = y + xy[i][1];
14210     int border_side = trigger_sides[i][1];
14211     int border_element = border_element_old[i];
14212
14213     if (border_element == -1)
14214       continue;
14215
14216     /* check for change of center element (but change it only once) */
14217     if (!change_center_element)
14218       change_center_element =
14219         CheckElementChangeBySide(x, y, center_element, border_element,
14220                                  CE_TOUCHING_X, border_side);
14221
14222 #if USE_FIX_CE_ACTION_WITH_PLAYER
14223     if (IS_PLAYER(xx, yy))
14224     {
14225       /* use player element that is initially defined in the level playfield,
14226          not the player element that corresponds to the runtime player number
14227          (example: a level that contains EL_PLAYER_3 as the only player would
14228          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14229       int player_element = PLAYERINFO(xx, yy)->initial_element;
14230
14231       CheckElementChangeBySide(x, y, center_element, player_element,
14232                                CE_TOUCHING_X, border_side);
14233     }
14234 #endif
14235   }
14236 }
14237
14238 #else
14239
14240 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14241 {
14242   static int xy[4][2] =
14243   {
14244     { 0, -1 },
14245     { -1, 0 },
14246     { +1, 0 },
14247     { 0, +1 }
14248   };
14249   static int trigger_sides[4][2] =
14250   {
14251     /* center side      border side */
14252     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14253     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14254     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14255     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14256   };
14257   static int touch_dir[4] =
14258   {
14259     MV_LEFT | MV_RIGHT,
14260     MV_UP   | MV_DOWN,
14261     MV_UP   | MV_DOWN,
14262     MV_LEFT | MV_RIGHT
14263   };
14264   boolean change_center_element = FALSE;
14265   int center_element = Feld[x][y];      /* should always be non-moving! */
14266   int i;
14267
14268   for (i = 0; i < NUM_DIRECTIONS; i++)
14269   {
14270     int xx = x + xy[i][0];
14271     int yy = y + xy[i][1];
14272     int center_side = trigger_sides[i][0];
14273     int border_side = trigger_sides[i][1];
14274     int border_element;
14275
14276     if (!IN_LEV_FIELD(xx, yy))
14277       continue;
14278
14279     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14280       border_element = Feld[xx][yy];    /* may be moving! */
14281     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14282       border_element = Feld[xx][yy];
14283     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14284       border_element = MovingOrBlocked2Element(xx, yy);
14285     else
14286       continue;                 /* center and border element do not touch */
14287
14288     /* check for change of center element (but change it only once) */
14289     if (!change_center_element)
14290       change_center_element =
14291         CheckElementChangeBySide(x, y, center_element, border_element,
14292                                  CE_TOUCHING_X, border_side);
14293
14294     /* check for change of border element */
14295     CheckElementChangeBySide(xx, yy, border_element, center_element,
14296                              CE_TOUCHING_X, center_side);
14297   }
14298 }
14299
14300 #endif
14301
14302 void TestIfElementHitsCustomElement(int x, int y, int direction)
14303 {
14304   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14305   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14306   int hitx = x + dx, hity = y + dy;
14307   int hitting_element = Feld[x][y];
14308   int touched_element;
14309
14310   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14311     return;
14312
14313   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14314                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14315
14316   if (IN_LEV_FIELD(hitx, hity))
14317   {
14318     int opposite_direction = MV_DIR_OPPOSITE(direction);
14319     int hitting_side = direction;
14320     int touched_side = opposite_direction;
14321     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14322                           MovDir[hitx][hity] != direction ||
14323                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14324
14325     object_hit = TRUE;
14326
14327     if (object_hit)
14328     {
14329       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14330                                CE_HITTING_X, touched_side);
14331
14332       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14333                                CE_HIT_BY_X, hitting_side);
14334
14335       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14336                                CE_HIT_BY_SOMETHING, opposite_direction);
14337
14338 #if USE_FIX_CE_ACTION_WITH_PLAYER
14339       if (IS_PLAYER(hitx, hity))
14340       {
14341         /* use player element that is initially defined in the level playfield,
14342            not the player element that corresponds to the runtime player number
14343            (example: a level that contains EL_PLAYER_3 as the only player would
14344            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14345         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14346
14347         CheckElementChangeBySide(x, y, hitting_element, player_element,
14348                                  CE_HITTING_X, touched_side);
14349       }
14350 #endif
14351     }
14352   }
14353
14354   /* "hitting something" is also true when hitting the playfield border */
14355   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14356                            CE_HITTING_SOMETHING, direction);
14357 }
14358
14359 #if 0
14360 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14361 {
14362   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14363   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14364   int hitx = x + dx, hity = y + dy;
14365   int hitting_element = Feld[x][y];
14366   int touched_element;
14367 #if 0
14368   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14369                         !IS_FREE(hitx, hity) &&
14370                         (!IS_MOVING(hitx, hity) ||
14371                          MovDir[hitx][hity] != direction ||
14372                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14373 #endif
14374
14375   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14376     return;
14377
14378 #if 0
14379   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14380     return;
14381 #endif
14382
14383   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14384                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14385
14386   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14387                            EP_CAN_SMASH_EVERYTHING, direction);
14388
14389   if (IN_LEV_FIELD(hitx, hity))
14390   {
14391     int opposite_direction = MV_DIR_OPPOSITE(direction);
14392     int hitting_side = direction;
14393     int touched_side = opposite_direction;
14394 #if 0
14395     int touched_element = MovingOrBlocked2Element(hitx, hity);
14396 #endif
14397 #if 1
14398     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14399                           MovDir[hitx][hity] != direction ||
14400                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14401
14402     object_hit = TRUE;
14403 #endif
14404
14405     if (object_hit)
14406     {
14407       int i;
14408
14409       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14410                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14411
14412       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14413                                CE_OTHER_IS_SMASHING, touched_side);
14414
14415       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14416                                CE_OTHER_GETS_SMASHED, hitting_side);
14417     }
14418   }
14419 }
14420 #endif
14421
14422 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14423 {
14424   int i, kill_x = -1, kill_y = -1;
14425
14426   int bad_element = -1;
14427   static int test_xy[4][2] =
14428   {
14429     { 0, -1 },
14430     { -1, 0 },
14431     { +1, 0 },
14432     { 0, +1 }
14433   };
14434   static int test_dir[4] =
14435   {
14436     MV_UP,
14437     MV_LEFT,
14438     MV_RIGHT,
14439     MV_DOWN
14440   };
14441
14442   for (i = 0; i < NUM_DIRECTIONS; i++)
14443   {
14444     int test_x, test_y, test_move_dir, test_element;
14445
14446     test_x = good_x + test_xy[i][0];
14447     test_y = good_y + test_xy[i][1];
14448
14449     if (!IN_LEV_FIELD(test_x, test_y))
14450       continue;
14451
14452     test_move_dir =
14453       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14454
14455     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14456
14457     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14458        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14459     */
14460     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14461         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14462     {
14463       kill_x = test_x;
14464       kill_y = test_y;
14465       bad_element = test_element;
14466
14467       break;
14468     }
14469   }
14470
14471   if (kill_x != -1 || kill_y != -1)
14472   {
14473     if (IS_PLAYER(good_x, good_y))
14474     {
14475       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14476
14477       if (player->shield_deadly_time_left > 0 &&
14478           !IS_INDESTRUCTIBLE(bad_element))
14479         Bang(kill_x, kill_y);
14480       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14481         KillPlayer(player);
14482     }
14483     else
14484       Bang(good_x, good_y);
14485   }
14486 }
14487
14488 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14489 {
14490   int i, kill_x = -1, kill_y = -1;
14491   int bad_element = Feld[bad_x][bad_y];
14492   static int test_xy[4][2] =
14493   {
14494     { 0, -1 },
14495     { -1, 0 },
14496     { +1, 0 },
14497     { 0, +1 }
14498   };
14499   static int touch_dir[4] =
14500   {
14501     MV_LEFT | MV_RIGHT,
14502     MV_UP   | MV_DOWN,
14503     MV_UP   | MV_DOWN,
14504     MV_LEFT | MV_RIGHT
14505   };
14506   static int test_dir[4] =
14507   {
14508     MV_UP,
14509     MV_LEFT,
14510     MV_RIGHT,
14511     MV_DOWN
14512   };
14513
14514   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14515     return;
14516
14517   for (i = 0; i < NUM_DIRECTIONS; i++)
14518   {
14519     int test_x, test_y, test_move_dir, test_element;
14520
14521     test_x = bad_x + test_xy[i][0];
14522     test_y = bad_y + test_xy[i][1];
14523
14524     if (!IN_LEV_FIELD(test_x, test_y))
14525       continue;
14526
14527     test_move_dir =
14528       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14529
14530     test_element = Feld[test_x][test_y];
14531
14532     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14533        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14534     */
14535     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14536         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14537     {
14538       /* good thing is player or penguin that does not move away */
14539       if (IS_PLAYER(test_x, test_y))
14540       {
14541         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14542
14543         if (bad_element == EL_ROBOT && player->is_moving)
14544           continue;     /* robot does not kill player if he is moving */
14545
14546         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14547         {
14548           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14549             continue;           /* center and border element do not touch */
14550         }
14551
14552         kill_x = test_x;
14553         kill_y = test_y;
14554
14555         break;
14556       }
14557       else if (test_element == EL_PENGUIN)
14558       {
14559         kill_x = test_x;
14560         kill_y = test_y;
14561
14562         break;
14563       }
14564     }
14565   }
14566
14567   if (kill_x != -1 || kill_y != -1)
14568   {
14569     if (IS_PLAYER(kill_x, kill_y))
14570     {
14571       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14572
14573       if (player->shield_deadly_time_left > 0 &&
14574           !IS_INDESTRUCTIBLE(bad_element))
14575         Bang(bad_x, bad_y);
14576       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14577         KillPlayer(player);
14578     }
14579     else
14580       Bang(kill_x, kill_y);
14581   }
14582 }
14583
14584 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14585 {
14586   int bad_element = Feld[bad_x][bad_y];
14587   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14588   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14589   int test_x = bad_x + dx, test_y = bad_y + dy;
14590   int test_move_dir, test_element;
14591   int kill_x = -1, kill_y = -1;
14592
14593   if (!IN_LEV_FIELD(test_x, test_y))
14594     return;
14595
14596   test_move_dir =
14597     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14598
14599   test_element = Feld[test_x][test_y];
14600
14601   if (test_move_dir != bad_move_dir)
14602   {
14603     /* good thing can be player or penguin that does not move away */
14604     if (IS_PLAYER(test_x, test_y))
14605     {
14606       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14607
14608       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14609          player as being hit when he is moving towards the bad thing, because
14610          the "get hit by" condition would be lost after the player stops) */
14611       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14612         return;         /* player moves away from bad thing */
14613
14614       kill_x = test_x;
14615       kill_y = test_y;
14616     }
14617     else if (test_element == EL_PENGUIN)
14618     {
14619       kill_x = test_x;
14620       kill_y = test_y;
14621     }
14622   }
14623
14624   if (kill_x != -1 || kill_y != -1)
14625   {
14626     if (IS_PLAYER(kill_x, kill_y))
14627     {
14628       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14629
14630       if (player->shield_deadly_time_left > 0 &&
14631           !IS_INDESTRUCTIBLE(bad_element))
14632         Bang(bad_x, bad_y);
14633       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14634         KillPlayer(player);
14635     }
14636     else
14637       Bang(kill_x, kill_y);
14638   }
14639 }
14640
14641 void TestIfPlayerTouchesBadThing(int x, int y)
14642 {
14643   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14644 }
14645
14646 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14647 {
14648   TestIfGoodThingHitsBadThing(x, y, move_dir);
14649 }
14650
14651 void TestIfBadThingTouchesPlayer(int x, int y)
14652 {
14653   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14654 }
14655
14656 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14657 {
14658   TestIfBadThingHitsGoodThing(x, y, move_dir);
14659 }
14660
14661 void TestIfFriendTouchesBadThing(int x, int y)
14662 {
14663   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14664 }
14665
14666 void TestIfBadThingTouchesFriend(int x, int y)
14667 {
14668   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14669 }
14670
14671 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14672 {
14673   int i, kill_x = bad_x, kill_y = bad_y;
14674   static int xy[4][2] =
14675   {
14676     { 0, -1 },
14677     { -1, 0 },
14678     { +1, 0 },
14679     { 0, +1 }
14680   };
14681
14682   for (i = 0; i < NUM_DIRECTIONS; i++)
14683   {
14684     int x, y, element;
14685
14686     x = bad_x + xy[i][0];
14687     y = bad_y + xy[i][1];
14688     if (!IN_LEV_FIELD(x, y))
14689       continue;
14690
14691     element = Feld[x][y];
14692     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14693         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14694     {
14695       kill_x = x;
14696       kill_y = y;
14697       break;
14698     }
14699   }
14700
14701   if (kill_x != bad_x || kill_y != bad_y)
14702     Bang(bad_x, bad_y);
14703 }
14704
14705 void KillPlayer(struct PlayerInfo *player)
14706 {
14707   int jx = player->jx, jy = player->jy;
14708
14709   if (!player->active)
14710     return;
14711
14712 #if 0
14713   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14714          player->killed, player->active, player->reanimated);
14715 #endif
14716
14717   /* the following code was introduced to prevent an infinite loop when calling
14718      -> Bang()
14719      -> CheckTriggeredElementChangeExt()
14720      -> ExecuteCustomElementAction()
14721      -> KillPlayer()
14722      -> (infinitely repeating the above sequence of function calls)
14723      which occurs when killing the player while having a CE with the setting
14724      "kill player X when explosion of <player X>"; the solution using a new
14725      field "player->killed" was chosen for backwards compatibility, although
14726      clever use of the fields "player->active" etc. would probably also work */
14727 #if 1
14728   if (player->killed)
14729     return;
14730 #endif
14731
14732   player->killed = TRUE;
14733
14734   /* remove accessible field at the player's position */
14735   Feld[jx][jy] = EL_EMPTY;
14736
14737   /* deactivate shield (else Bang()/Explode() would not work right) */
14738   player->shield_normal_time_left = 0;
14739   player->shield_deadly_time_left = 0;
14740
14741 #if 0
14742   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14743          player->killed, player->active, player->reanimated);
14744 #endif
14745
14746   Bang(jx, jy);
14747
14748 #if 0
14749   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14750          player->killed, player->active, player->reanimated);
14751 #endif
14752
14753 #if USE_PLAYER_REANIMATION
14754 #if 1
14755   if (player->reanimated)       /* killed player may have been reanimated */
14756     player->killed = player->reanimated = FALSE;
14757   else
14758     BuryPlayer(player);
14759 #else
14760   if (player->killed)           /* player may have been reanimated */
14761     BuryPlayer(player);
14762 #endif
14763 #else
14764   BuryPlayer(player);
14765 #endif
14766 }
14767
14768 static void KillPlayerUnlessEnemyProtected(int x, int y)
14769 {
14770   if (!PLAYER_ENEMY_PROTECTED(x, y))
14771     KillPlayer(PLAYERINFO(x, y));
14772 }
14773
14774 static void KillPlayerUnlessExplosionProtected(int x, int y)
14775 {
14776   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14777     KillPlayer(PLAYERINFO(x, y));
14778 }
14779
14780 void BuryPlayer(struct PlayerInfo *player)
14781 {
14782   int jx = player->jx, jy = player->jy;
14783
14784   if (!player->active)
14785     return;
14786
14787   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14788   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14789
14790   player->GameOver = TRUE;
14791   RemovePlayer(player);
14792 }
14793
14794 void RemovePlayer(struct PlayerInfo *player)
14795 {
14796   int jx = player->jx, jy = player->jy;
14797   int i, found = FALSE;
14798
14799   player->present = FALSE;
14800   player->active = FALSE;
14801
14802   if (!ExplodeField[jx][jy])
14803     StorePlayer[jx][jy] = 0;
14804
14805   if (player->is_moving)
14806     TEST_DrawLevelField(player->last_jx, player->last_jy);
14807
14808   for (i = 0; i < MAX_PLAYERS; i++)
14809     if (stored_player[i].active)
14810       found = TRUE;
14811
14812   if (!found)
14813     AllPlayersGone = TRUE;
14814
14815   ExitX = ZX = jx;
14816   ExitY = ZY = jy;
14817 }
14818
14819 #if USE_NEW_SNAP_DELAY
14820 static void setFieldForSnapping(int x, int y, int element, int direction)
14821 {
14822   struct ElementInfo *ei = &element_info[element];
14823   int direction_bit = MV_DIR_TO_BIT(direction);
14824   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14825   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14826                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14827
14828   Feld[x][y] = EL_ELEMENT_SNAPPING;
14829   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14830
14831   ResetGfxAnimation(x, y);
14832
14833   GfxElement[x][y] = element;
14834   GfxAction[x][y] = action;
14835   GfxDir[x][y] = direction;
14836   GfxFrame[x][y] = -1;
14837 }
14838 #endif
14839
14840 /*
14841   =============================================================================
14842   checkDiagonalPushing()
14843   -----------------------------------------------------------------------------
14844   check if diagonal input device direction results in pushing of object
14845   (by checking if the alternative direction is walkable, diggable, ...)
14846   =============================================================================
14847 */
14848
14849 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14850                                     int x, int y, int real_dx, int real_dy)
14851 {
14852   int jx, jy, dx, dy, xx, yy;
14853
14854   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14855     return TRUE;
14856
14857   /* diagonal direction: check alternative direction */
14858   jx = player->jx;
14859   jy = player->jy;
14860   dx = x - jx;
14861   dy = y - jy;
14862   xx = jx + (dx == 0 ? real_dx : 0);
14863   yy = jy + (dy == 0 ? real_dy : 0);
14864
14865   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14866 }
14867
14868 /*
14869   =============================================================================
14870   DigField()
14871   -----------------------------------------------------------------------------
14872   x, y:                 field next to player (non-diagonal) to try to dig to
14873   real_dx, real_dy:     direction as read from input device (can be diagonal)
14874   =============================================================================
14875 */
14876
14877 static int DigField(struct PlayerInfo *player,
14878                     int oldx, int oldy, int x, int y,
14879                     int real_dx, int real_dy, int mode)
14880 {
14881   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14882   boolean player_was_pushing = player->is_pushing;
14883   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14884   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14885   int jx = oldx, jy = oldy;
14886   int dx = x - jx, dy = y - jy;
14887   int nextx = x + dx, nexty = y + dy;
14888   int move_direction = (dx == -1 ? MV_LEFT  :
14889                         dx == +1 ? MV_RIGHT :
14890                         dy == -1 ? MV_UP    :
14891                         dy == +1 ? MV_DOWN  : MV_NONE);
14892   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14893   int dig_side = MV_DIR_OPPOSITE(move_direction);
14894   int old_element = Feld[jx][jy];
14895 #if USE_FIXED_DONT_RUN_INTO
14896   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14897 #else
14898   int element;
14899 #endif
14900   int collect_count;
14901
14902   if (is_player)                /* function can also be called by EL_PENGUIN */
14903   {
14904     if (player->MovPos == 0)
14905     {
14906       player->is_digging = FALSE;
14907       player->is_collecting = FALSE;
14908     }
14909
14910     if (player->MovPos == 0)    /* last pushing move finished */
14911       player->is_pushing = FALSE;
14912
14913     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14914     {
14915       player->is_switching = FALSE;
14916       player->push_delay = -1;
14917
14918       return MP_NO_ACTION;
14919     }
14920   }
14921
14922 #if !USE_FIXED_DONT_RUN_INTO
14923   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14924     return MP_NO_ACTION;
14925 #endif
14926
14927   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14928     old_element = Back[jx][jy];
14929
14930   /* in case of element dropped at player position, check background */
14931   else if (Back[jx][jy] != EL_EMPTY &&
14932            game.engine_version >= VERSION_IDENT(2,2,0,0))
14933     old_element = Back[jx][jy];
14934
14935   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14936     return MP_NO_ACTION;        /* field has no opening in this direction */
14937
14938   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14939     return MP_NO_ACTION;        /* field has no opening in this direction */
14940
14941 #if USE_FIXED_DONT_RUN_INTO
14942   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14943   {
14944     SplashAcid(x, y);
14945
14946     Feld[jx][jy] = player->artwork_element;
14947     InitMovingField(jx, jy, MV_DOWN);
14948     Store[jx][jy] = EL_ACID;
14949     ContinueMoving(jx, jy);
14950     BuryPlayer(player);
14951
14952     return MP_DONT_RUN_INTO;
14953   }
14954 #endif
14955
14956 #if USE_FIXED_DONT_RUN_INTO
14957   if (player_can_move && DONT_RUN_INTO(element))
14958   {
14959     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14960
14961     return MP_DONT_RUN_INTO;
14962   }
14963 #endif
14964
14965 #if USE_FIXED_DONT_RUN_INTO
14966   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14967     return MP_NO_ACTION;
14968 #endif
14969
14970 #if !USE_FIXED_DONT_RUN_INTO
14971   element = Feld[x][y];
14972 #endif
14973
14974   collect_count = element_info[element].collect_count_initial;
14975
14976   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14977     return MP_NO_ACTION;
14978
14979   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14980     player_can_move = player_can_move_or_snap;
14981
14982   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14983       game.engine_version >= VERSION_IDENT(2,2,0,0))
14984   {
14985     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14986                                player->index_bit, dig_side);
14987     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14988                                         player->index_bit, dig_side);
14989
14990     if (element == EL_DC_LANDMINE)
14991       Bang(x, y);
14992
14993     if (Feld[x][y] != element)          /* field changed by snapping */
14994       return MP_ACTION;
14995
14996     return MP_NO_ACTION;
14997   }
14998
14999 #if USE_PLAYER_GRAVITY
15000   if (player->gravity && is_player && !player->is_auto_moving &&
15001       canFallDown(player) && move_direction != MV_DOWN &&
15002       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15003     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15004 #else
15005   if (game.gravity && is_player && !player->is_auto_moving &&
15006       canFallDown(player) && move_direction != MV_DOWN &&
15007       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15008     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15009 #endif
15010
15011   if (player_can_move &&
15012       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15013   {
15014     int sound_element = SND_ELEMENT(element);
15015     int sound_action = ACTION_WALKING;
15016
15017     if (IS_RND_GATE(element))
15018     {
15019       if (!player->key[RND_GATE_NR(element)])
15020         return MP_NO_ACTION;
15021     }
15022     else if (IS_RND_GATE_GRAY(element))
15023     {
15024       if (!player->key[RND_GATE_GRAY_NR(element)])
15025         return MP_NO_ACTION;
15026     }
15027     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15028     {
15029       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15030         return MP_NO_ACTION;
15031     }
15032     else if (element == EL_EXIT_OPEN ||
15033              element == EL_EM_EXIT_OPEN ||
15034 #if 1
15035              element == EL_EM_EXIT_OPENING ||
15036 #endif
15037              element == EL_STEEL_EXIT_OPEN ||
15038              element == EL_EM_STEEL_EXIT_OPEN ||
15039 #if 1
15040              element == EL_EM_STEEL_EXIT_OPENING ||
15041 #endif
15042              element == EL_SP_EXIT_OPEN ||
15043              element == EL_SP_EXIT_OPENING)
15044     {
15045       sound_action = ACTION_PASSING;    /* player is passing exit */
15046     }
15047     else if (element == EL_EMPTY)
15048     {
15049       sound_action = ACTION_MOVING;             /* nothing to walk on */
15050     }
15051
15052     /* play sound from background or player, whatever is available */
15053     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15054       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15055     else
15056       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15057   }
15058   else if (player_can_move &&
15059            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15060   {
15061     if (!ACCESS_FROM(element, opposite_direction))
15062       return MP_NO_ACTION;      /* field not accessible from this direction */
15063
15064     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15065       return MP_NO_ACTION;
15066
15067     if (IS_EM_GATE(element))
15068     {
15069       if (!player->key[EM_GATE_NR(element)])
15070         return MP_NO_ACTION;
15071     }
15072     else if (IS_EM_GATE_GRAY(element))
15073     {
15074       if (!player->key[EM_GATE_GRAY_NR(element)])
15075         return MP_NO_ACTION;
15076     }
15077     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15078     {
15079       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15080         return MP_NO_ACTION;
15081     }
15082     else if (IS_EMC_GATE(element))
15083     {
15084       if (!player->key[EMC_GATE_NR(element)])
15085         return MP_NO_ACTION;
15086     }
15087     else if (IS_EMC_GATE_GRAY(element))
15088     {
15089       if (!player->key[EMC_GATE_GRAY_NR(element)])
15090         return MP_NO_ACTION;
15091     }
15092     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15093     {
15094       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15095         return MP_NO_ACTION;
15096     }
15097     else if (element == EL_DC_GATE_WHITE ||
15098              element == EL_DC_GATE_WHITE_GRAY ||
15099              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15100     {
15101       if (player->num_white_keys == 0)
15102         return MP_NO_ACTION;
15103
15104       player->num_white_keys--;
15105     }
15106     else if (IS_SP_PORT(element))
15107     {
15108       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15109           element == EL_SP_GRAVITY_PORT_RIGHT ||
15110           element == EL_SP_GRAVITY_PORT_UP ||
15111           element == EL_SP_GRAVITY_PORT_DOWN)
15112 #if USE_PLAYER_GRAVITY
15113         player->gravity = !player->gravity;
15114 #else
15115         game.gravity = !game.gravity;
15116 #endif
15117       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15118                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15119                element == EL_SP_GRAVITY_ON_PORT_UP ||
15120                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15121 #if USE_PLAYER_GRAVITY
15122         player->gravity = TRUE;
15123 #else
15124         game.gravity = TRUE;
15125 #endif
15126       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15127                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15128                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15129                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15130 #if USE_PLAYER_GRAVITY
15131         player->gravity = FALSE;
15132 #else
15133         game.gravity = FALSE;
15134 #endif
15135     }
15136
15137     /* automatically move to the next field with double speed */
15138     player->programmed_action = move_direction;
15139
15140     if (player->move_delay_reset_counter == 0)
15141     {
15142       player->move_delay_reset_counter = 2;     /* two double speed steps */
15143
15144       DOUBLE_PLAYER_SPEED(player);
15145     }
15146
15147     PlayLevelSoundAction(x, y, ACTION_PASSING);
15148   }
15149   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15150   {
15151     RemoveField(x, y);
15152
15153     if (mode != DF_SNAP)
15154     {
15155       GfxElement[x][y] = GFX_ELEMENT(element);
15156       player->is_digging = TRUE;
15157     }
15158
15159     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15160
15161     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15162                                         player->index_bit, dig_side);
15163
15164     if (mode == DF_SNAP)
15165     {
15166 #if USE_NEW_SNAP_DELAY
15167       if (level.block_snap_field)
15168         setFieldForSnapping(x, y, element, move_direction);
15169       else
15170         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15171 #else
15172       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15173 #endif
15174
15175       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15176                                           player->index_bit, dig_side);
15177     }
15178   }
15179   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15180   {
15181     RemoveField(x, y);
15182
15183     if (is_player && mode != DF_SNAP)
15184     {
15185       GfxElement[x][y] = element;
15186       player->is_collecting = TRUE;
15187     }
15188
15189     if (element == EL_SPEED_PILL)
15190     {
15191       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15192     }
15193     else if (element == EL_EXTRA_TIME && level.time > 0)
15194     {
15195       TimeLeft += level.extra_time;
15196
15197 #if 1
15198       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15199
15200       DisplayGameControlValues();
15201 #else
15202       DrawGameValue_Time(TimeLeft);
15203 #endif
15204     }
15205     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15206     {
15207       player->shield_normal_time_left += level.shield_normal_time;
15208       if (element == EL_SHIELD_DEADLY)
15209         player->shield_deadly_time_left += level.shield_deadly_time;
15210     }
15211     else if (element == EL_DYNAMITE ||
15212              element == EL_EM_DYNAMITE ||
15213              element == EL_SP_DISK_RED)
15214     {
15215       if (player->inventory_size < MAX_INVENTORY_SIZE)
15216         player->inventory_element[player->inventory_size++] = element;
15217
15218       DrawGameDoorValues();
15219     }
15220     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15221     {
15222       player->dynabomb_count++;
15223       player->dynabombs_left++;
15224     }
15225     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15226     {
15227       player->dynabomb_size++;
15228     }
15229     else if (element == EL_DYNABOMB_INCREASE_POWER)
15230     {
15231       player->dynabomb_xl = TRUE;
15232     }
15233     else if (IS_KEY(element))
15234     {
15235       player->key[KEY_NR(element)] = TRUE;
15236
15237       DrawGameDoorValues();
15238     }
15239     else if (element == EL_DC_KEY_WHITE)
15240     {
15241       player->num_white_keys++;
15242
15243       /* display white keys? */
15244       /* DrawGameDoorValues(); */
15245     }
15246     else if (IS_ENVELOPE(element))
15247     {
15248       player->show_envelope = element;
15249     }
15250     else if (element == EL_EMC_LENSES)
15251     {
15252       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15253
15254       RedrawAllInvisibleElementsForLenses();
15255     }
15256     else if (element == EL_EMC_MAGNIFIER)
15257     {
15258       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15259
15260       RedrawAllInvisibleElementsForMagnifier();
15261     }
15262     else if (IS_DROPPABLE(element) ||
15263              IS_THROWABLE(element))     /* can be collected and dropped */
15264     {
15265       int i;
15266
15267       if (collect_count == 0)
15268         player->inventory_infinite_element = element;
15269       else
15270         for (i = 0; i < collect_count; i++)
15271           if (player->inventory_size < MAX_INVENTORY_SIZE)
15272             player->inventory_element[player->inventory_size++] = element;
15273
15274       DrawGameDoorValues();
15275     }
15276     else if (collect_count > 0)
15277     {
15278       local_player->gems_still_needed -= collect_count;
15279       if (local_player->gems_still_needed < 0)
15280         local_player->gems_still_needed = 0;
15281
15282 #if 1
15283       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15284
15285       DisplayGameControlValues();
15286 #else
15287       DrawGameValue_Emeralds(local_player->gems_still_needed);
15288 #endif
15289     }
15290
15291     RaiseScoreElement(element);
15292     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15293
15294     if (is_player)
15295       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15296                                           player->index_bit, dig_side);
15297
15298     if (mode == DF_SNAP)
15299     {
15300 #if USE_NEW_SNAP_DELAY
15301       if (level.block_snap_field)
15302         setFieldForSnapping(x, y, element, move_direction);
15303       else
15304         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15305 #else
15306       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15307 #endif
15308
15309       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15310                                           player->index_bit, dig_side);
15311     }
15312   }
15313   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15314   {
15315     if (mode == DF_SNAP && element != EL_BD_ROCK)
15316       return MP_NO_ACTION;
15317
15318     if (CAN_FALL(element) && dy)
15319       return MP_NO_ACTION;
15320
15321     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15322         !(element == EL_SPRING && level.use_spring_bug))
15323       return MP_NO_ACTION;
15324
15325     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15326         ((move_direction & MV_VERTICAL &&
15327           ((element_info[element].move_pattern & MV_LEFT &&
15328             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15329            (element_info[element].move_pattern & MV_RIGHT &&
15330             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15331          (move_direction & MV_HORIZONTAL &&
15332           ((element_info[element].move_pattern & MV_UP &&
15333             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15334            (element_info[element].move_pattern & MV_DOWN &&
15335             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15336       return MP_NO_ACTION;
15337
15338     /* do not push elements already moving away faster than player */
15339     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15340         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15341       return MP_NO_ACTION;
15342
15343     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15344     {
15345       if (player->push_delay_value == -1 || !player_was_pushing)
15346         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15347     }
15348     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15349     {
15350       if (player->push_delay_value == -1)
15351         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15352     }
15353     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15354     {
15355       if (!player->is_pushing)
15356         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15357     }
15358
15359     player->is_pushing = TRUE;
15360     player->is_active = TRUE;
15361
15362     if (!(IN_LEV_FIELD(nextx, nexty) &&
15363           (IS_FREE(nextx, nexty) ||
15364            (IS_SB_ELEMENT(element) &&
15365             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15366            (IS_CUSTOM_ELEMENT(element) &&
15367             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15368       return MP_NO_ACTION;
15369
15370     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15371       return MP_NO_ACTION;
15372
15373     if (player->push_delay == -1)       /* new pushing; restart delay */
15374       player->push_delay = 0;
15375
15376     if (player->push_delay < player->push_delay_value &&
15377         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15378         element != EL_SPRING && element != EL_BALLOON)
15379     {
15380       /* make sure that there is no move delay before next try to push */
15381       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15382         player->move_delay = 0;
15383
15384       return MP_NO_ACTION;
15385     }
15386
15387     if (IS_CUSTOM_ELEMENT(element) &&
15388         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15389     {
15390       if (!DigFieldByCE(nextx, nexty, element))
15391         return MP_NO_ACTION;
15392     }
15393
15394     if (IS_SB_ELEMENT(element))
15395     {
15396       if (element == EL_SOKOBAN_FIELD_FULL)
15397       {
15398         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15399         local_player->sokobanfields_still_needed++;
15400       }
15401
15402       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15403       {
15404         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15405         local_player->sokobanfields_still_needed--;
15406       }
15407
15408       Feld[x][y] = EL_SOKOBAN_OBJECT;
15409
15410       if (Back[x][y] == Back[nextx][nexty])
15411         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15412       else if (Back[x][y] != 0)
15413         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15414                                     ACTION_EMPTYING);
15415       else
15416         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15417                                     ACTION_FILLING);
15418
15419 #if 1
15420       if (local_player->sokobanfields_still_needed == 0 &&
15421           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15422 #else
15423       if (local_player->sokobanfields_still_needed == 0 &&
15424           game.emulation == EMU_SOKOBAN)
15425 #endif
15426       {
15427         PlayerWins(player);
15428
15429         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15430       }
15431     }
15432     else
15433       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15434
15435     InitMovingField(x, y, move_direction);
15436     GfxAction[x][y] = ACTION_PUSHING;
15437
15438     if (mode == DF_SNAP)
15439       ContinueMoving(x, y);
15440     else
15441       MovPos[x][y] = (dx != 0 ? dx : dy);
15442
15443     Pushed[x][y] = TRUE;
15444     Pushed[nextx][nexty] = TRUE;
15445
15446     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15447       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15448     else
15449       player->push_delay_value = -1;    /* get new value later */
15450
15451     /* check for element change _after_ element has been pushed */
15452     if (game.use_change_when_pushing_bug)
15453     {
15454       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15455                                  player->index_bit, dig_side);
15456       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15457                                           player->index_bit, dig_side);
15458     }
15459   }
15460   else if (IS_SWITCHABLE(element))
15461   {
15462     if (PLAYER_SWITCHING(player, x, y))
15463     {
15464       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15465                                           player->index_bit, dig_side);
15466
15467       return MP_ACTION;
15468     }
15469
15470     player->is_switching = TRUE;
15471     player->switch_x = x;
15472     player->switch_y = y;
15473
15474     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15475
15476     if (element == EL_ROBOT_WHEEL)
15477     {
15478       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15479       ZX = x;
15480       ZY = y;
15481
15482       game.robot_wheel_active = TRUE;
15483
15484       TEST_DrawLevelField(x, y);
15485     }
15486     else if (element == EL_SP_TERMINAL)
15487     {
15488       int xx, yy;
15489
15490       SCAN_PLAYFIELD(xx, yy)
15491       {
15492         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15493           Bang(xx, yy);
15494         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15495           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15496       }
15497     }
15498     else if (IS_BELT_SWITCH(element))
15499     {
15500       ToggleBeltSwitch(x, y);
15501     }
15502     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15503              element == EL_SWITCHGATE_SWITCH_DOWN ||
15504              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15505              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15506     {
15507       ToggleSwitchgateSwitch(x, y);
15508     }
15509     else if (element == EL_LIGHT_SWITCH ||
15510              element == EL_LIGHT_SWITCH_ACTIVE)
15511     {
15512       ToggleLightSwitch(x, y);
15513     }
15514     else if (element == EL_TIMEGATE_SWITCH ||
15515              element == EL_DC_TIMEGATE_SWITCH)
15516     {
15517       ActivateTimegateSwitch(x, y);
15518     }
15519     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15520              element == EL_BALLOON_SWITCH_RIGHT ||
15521              element == EL_BALLOON_SWITCH_UP    ||
15522              element == EL_BALLOON_SWITCH_DOWN  ||
15523              element == EL_BALLOON_SWITCH_NONE  ||
15524              element == EL_BALLOON_SWITCH_ANY)
15525     {
15526       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15527                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15528                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15529                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15530                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15531                              move_direction);
15532     }
15533     else if (element == EL_LAMP)
15534     {
15535       Feld[x][y] = EL_LAMP_ACTIVE;
15536       local_player->lights_still_needed--;
15537
15538       ResetGfxAnimation(x, y);
15539       TEST_DrawLevelField(x, y);
15540     }
15541     else if (element == EL_TIME_ORB_FULL)
15542     {
15543       Feld[x][y] = EL_TIME_ORB_EMPTY;
15544
15545       if (level.time > 0 || level.use_time_orb_bug)
15546       {
15547         TimeLeft += level.time_orb_time;
15548
15549 #if 1
15550         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15551
15552         DisplayGameControlValues();
15553 #else
15554         DrawGameValue_Time(TimeLeft);
15555 #endif
15556       }
15557
15558       ResetGfxAnimation(x, y);
15559       TEST_DrawLevelField(x, y);
15560     }
15561     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15562              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15563     {
15564       int xx, yy;
15565
15566       game.ball_state = !game.ball_state;
15567
15568       SCAN_PLAYFIELD(xx, yy)
15569       {
15570         int e = Feld[xx][yy];
15571
15572         if (game.ball_state)
15573         {
15574           if (e == EL_EMC_MAGIC_BALL)
15575             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15576           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15577             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15578         }
15579         else
15580         {
15581           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15582             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15583           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15584             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15585         }
15586       }
15587     }
15588
15589     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15590                                         player->index_bit, dig_side);
15591
15592     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15593                                         player->index_bit, dig_side);
15594
15595     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15596                                         player->index_bit, dig_side);
15597
15598     return MP_ACTION;
15599   }
15600   else
15601   {
15602     if (!PLAYER_SWITCHING(player, x, y))
15603     {
15604       player->is_switching = TRUE;
15605       player->switch_x = x;
15606       player->switch_y = y;
15607
15608       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15609                                  player->index_bit, dig_side);
15610       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15611                                           player->index_bit, dig_side);
15612
15613       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15614                                  player->index_bit, dig_side);
15615       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15616                                           player->index_bit, dig_side);
15617     }
15618
15619     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15620                                player->index_bit, dig_side);
15621     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15622                                         player->index_bit, dig_side);
15623
15624     return MP_NO_ACTION;
15625   }
15626
15627   player->push_delay = -1;
15628
15629   if (is_player)                /* function can also be called by EL_PENGUIN */
15630   {
15631     if (Feld[x][y] != element)          /* really digged/collected something */
15632     {
15633       player->is_collecting = !player->is_digging;
15634       player->is_active = TRUE;
15635     }
15636   }
15637
15638   return MP_MOVING;
15639 }
15640
15641 static boolean DigFieldByCE(int x, int y, int digging_element)
15642 {
15643   int element = Feld[x][y];
15644
15645   if (!IS_FREE(x, y))
15646   {
15647     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15648                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15649                   ACTION_BREAKING);
15650
15651     /* no element can dig solid indestructible elements */
15652     if (IS_INDESTRUCTIBLE(element) &&
15653         !IS_DIGGABLE(element) &&
15654         !IS_COLLECTIBLE(element))
15655       return FALSE;
15656
15657     if (AmoebaNr[x][y] &&
15658         (element == EL_AMOEBA_FULL ||
15659          element == EL_BD_AMOEBA ||
15660          element == EL_AMOEBA_GROWING))
15661     {
15662       AmoebaCnt[AmoebaNr[x][y]]--;
15663       AmoebaCnt2[AmoebaNr[x][y]]--;
15664     }
15665
15666     if (IS_MOVING(x, y))
15667       RemoveMovingField(x, y);
15668     else
15669     {
15670       RemoveField(x, y);
15671       TEST_DrawLevelField(x, y);
15672     }
15673
15674     /* if digged element was about to explode, prevent the explosion */
15675     ExplodeField[x][y] = EX_TYPE_NONE;
15676
15677     PlayLevelSoundAction(x, y, action);
15678   }
15679
15680   Store[x][y] = EL_EMPTY;
15681
15682 #if 1
15683   /* this makes it possible to leave the removed element again */
15684   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15685     Store[x][y] = element;
15686 #else
15687   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15688   {
15689     int move_leave_element = element_info[digging_element].move_leave_element;
15690
15691     /* this makes it possible to leave the removed element again */
15692     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15693                    element : move_leave_element);
15694   }
15695 #endif
15696
15697   return TRUE;
15698 }
15699
15700 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15701 {
15702   int jx = player->jx, jy = player->jy;
15703   int x = jx + dx, y = jy + dy;
15704   int snap_direction = (dx == -1 ? MV_LEFT  :
15705                         dx == +1 ? MV_RIGHT :
15706                         dy == -1 ? MV_UP    :
15707                         dy == +1 ? MV_DOWN  : MV_NONE);
15708   boolean can_continue_snapping = (level.continuous_snapping &&
15709                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15710
15711   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15712     return FALSE;
15713
15714   if (!player->active || !IN_LEV_FIELD(x, y))
15715     return FALSE;
15716
15717   if (dx && dy)
15718     return FALSE;
15719
15720   if (!dx && !dy)
15721   {
15722     if (player->MovPos == 0)
15723       player->is_pushing = FALSE;
15724
15725     player->is_snapping = FALSE;
15726
15727     if (player->MovPos == 0)
15728     {
15729       player->is_moving = FALSE;
15730       player->is_digging = FALSE;
15731       player->is_collecting = FALSE;
15732     }
15733
15734     return FALSE;
15735   }
15736
15737 #if USE_NEW_CONTINUOUS_SNAPPING
15738   /* prevent snapping with already pressed snap key when not allowed */
15739   if (player->is_snapping && !can_continue_snapping)
15740     return FALSE;
15741 #else
15742   if (player->is_snapping)
15743     return FALSE;
15744 #endif
15745
15746   player->MovDir = snap_direction;
15747
15748   if (player->MovPos == 0)
15749   {
15750     player->is_moving = FALSE;
15751     player->is_digging = FALSE;
15752     player->is_collecting = FALSE;
15753   }
15754
15755   player->is_dropping = FALSE;
15756   player->is_dropping_pressed = FALSE;
15757   player->drop_pressed_delay = 0;
15758
15759   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15760     return FALSE;
15761
15762   player->is_snapping = TRUE;
15763   player->is_active = TRUE;
15764
15765   if (player->MovPos == 0)
15766   {
15767     player->is_moving = FALSE;
15768     player->is_digging = FALSE;
15769     player->is_collecting = FALSE;
15770   }
15771
15772   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15773     TEST_DrawLevelField(player->last_jx, player->last_jy);
15774
15775   TEST_DrawLevelField(x, y);
15776
15777   return TRUE;
15778 }
15779
15780 static boolean DropElement(struct PlayerInfo *player)
15781 {
15782   int old_element, new_element;
15783   int dropx = player->jx, dropy = player->jy;
15784   int drop_direction = player->MovDir;
15785   int drop_side = drop_direction;
15786 #if 1
15787   int drop_element = get_next_dropped_element(player);
15788 #else
15789   int drop_element = (player->inventory_size > 0 ?
15790                       player->inventory_element[player->inventory_size - 1] :
15791                       player->inventory_infinite_element != EL_UNDEFINED ?
15792                       player->inventory_infinite_element :
15793                       player->dynabombs_left > 0 ?
15794                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15795                       EL_UNDEFINED);
15796 #endif
15797
15798   player->is_dropping_pressed = TRUE;
15799
15800   /* do not drop an element on top of another element; when holding drop key
15801      pressed without moving, dropped element must move away before the next
15802      element can be dropped (this is especially important if the next element
15803      is dynamite, which can be placed on background for historical reasons) */
15804   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15805     return MP_ACTION;
15806
15807   if (IS_THROWABLE(drop_element))
15808   {
15809     dropx += GET_DX_FROM_DIR(drop_direction);
15810     dropy += GET_DY_FROM_DIR(drop_direction);
15811
15812     if (!IN_LEV_FIELD(dropx, dropy))
15813       return FALSE;
15814   }
15815
15816   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15817   new_element = drop_element;           /* default: no change when dropping */
15818
15819   /* check if player is active, not moving and ready to drop */
15820   if (!player->active || player->MovPos || player->drop_delay > 0)
15821     return FALSE;
15822
15823   /* check if player has anything that can be dropped */
15824   if (new_element == EL_UNDEFINED)
15825     return FALSE;
15826
15827   /* check if drop key was pressed long enough for EM style dynamite */
15828   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15829     return FALSE;
15830
15831   /* check if anything can be dropped at the current position */
15832   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15833     return FALSE;
15834
15835   /* collected custom elements can only be dropped on empty fields */
15836   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15837     return FALSE;
15838
15839   if (old_element != EL_EMPTY)
15840     Back[dropx][dropy] = old_element;   /* store old element on this field */
15841
15842   ResetGfxAnimation(dropx, dropy);
15843   ResetRandomAnimationValue(dropx, dropy);
15844
15845   if (player->inventory_size > 0 ||
15846       player->inventory_infinite_element != EL_UNDEFINED)
15847   {
15848     if (player->inventory_size > 0)
15849     {
15850       player->inventory_size--;
15851
15852       DrawGameDoorValues();
15853
15854       if (new_element == EL_DYNAMITE)
15855         new_element = EL_DYNAMITE_ACTIVE;
15856       else if (new_element == EL_EM_DYNAMITE)
15857         new_element = EL_EM_DYNAMITE_ACTIVE;
15858       else if (new_element == EL_SP_DISK_RED)
15859         new_element = EL_SP_DISK_RED_ACTIVE;
15860     }
15861
15862     Feld[dropx][dropy] = new_element;
15863
15864     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15865       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15866                           el2img(Feld[dropx][dropy]), 0);
15867
15868     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15869
15870     /* needed if previous element just changed to "empty" in the last frame */
15871     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15872
15873     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15874                                player->index_bit, drop_side);
15875     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15876                                         CE_PLAYER_DROPS_X,
15877                                         player->index_bit, drop_side);
15878
15879     TestIfElementTouchesCustomElement(dropx, dropy);
15880   }
15881   else          /* player is dropping a dyna bomb */
15882   {
15883     player->dynabombs_left--;
15884
15885     Feld[dropx][dropy] = new_element;
15886
15887     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15888       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15889                           el2img(Feld[dropx][dropy]), 0);
15890
15891     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15892   }
15893
15894   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15895     InitField_WithBug1(dropx, dropy, FALSE);
15896
15897   new_element = Feld[dropx][dropy];     /* element might have changed */
15898
15899   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15900       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15901   {
15902     int move_direction, nextx, nexty;
15903
15904     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15905       MovDir[dropx][dropy] = drop_direction;
15906
15907     move_direction = MovDir[dropx][dropy];
15908     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15909     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15910
15911     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15912
15913 #if USE_FIX_IMPACT_COLLISION
15914     /* do not cause impact style collision by dropping elements that can fall */
15915     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15916 #else
15917     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15918 #endif
15919   }
15920
15921   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15922   player->is_dropping = TRUE;
15923
15924   player->drop_pressed_delay = 0;
15925   player->is_dropping_pressed = FALSE;
15926
15927   player->drop_x = dropx;
15928   player->drop_y = dropy;
15929
15930   return TRUE;
15931 }
15932
15933 /* ------------------------------------------------------------------------- */
15934 /* game sound playing functions                                              */
15935 /* ------------------------------------------------------------------------- */
15936
15937 static int *loop_sound_frame = NULL;
15938 static int *loop_sound_volume = NULL;
15939
15940 void InitPlayLevelSound()
15941 {
15942   int num_sounds = getSoundListSize();
15943
15944   checked_free(loop_sound_frame);
15945   checked_free(loop_sound_volume);
15946
15947   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15948   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15949 }
15950
15951 static void PlayLevelSound(int x, int y, int nr)
15952 {
15953   int sx = SCREENX(x), sy = SCREENY(y);
15954   int volume, stereo_position;
15955   int max_distance = 8;
15956   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15957
15958   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15959       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15960     return;
15961
15962   if (!IN_LEV_FIELD(x, y) ||
15963       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15964       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15965     return;
15966
15967   volume = SOUND_MAX_VOLUME;
15968
15969   if (!IN_SCR_FIELD(sx, sy))
15970   {
15971     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15972     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15973
15974     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15975   }
15976
15977   stereo_position = (SOUND_MAX_LEFT +
15978                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15979                      (SCR_FIELDX + 2 * max_distance));
15980
15981   if (IS_LOOP_SOUND(nr))
15982   {
15983     /* This assures that quieter loop sounds do not overwrite louder ones,
15984        while restarting sound volume comparison with each new game frame. */
15985
15986     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15987       return;
15988
15989     loop_sound_volume[nr] = volume;
15990     loop_sound_frame[nr] = FrameCounter;
15991   }
15992
15993   PlaySoundExt(nr, volume, stereo_position, type);
15994 }
15995
15996 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15997 {
15998   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15999                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16000                  y < LEVELY(BY1) ? LEVELY(BY1) :
16001                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16002                  sound_action);
16003 }
16004
16005 static void PlayLevelSoundAction(int x, int y, int action)
16006 {
16007   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16008 }
16009
16010 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16011 {
16012   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16013
16014   if (sound_effect != SND_UNDEFINED)
16015     PlayLevelSound(x, y, sound_effect);
16016 }
16017
16018 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16019                                               int action)
16020 {
16021   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16022
16023   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16024     PlayLevelSound(x, y, sound_effect);
16025 }
16026
16027 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16028 {
16029   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16030
16031   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16032     PlayLevelSound(x, y, sound_effect);
16033 }
16034
16035 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16036 {
16037   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16038
16039   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16040     StopSound(sound_effect);
16041 }
16042
16043 static void PlayLevelMusic()
16044 {
16045   if (levelset.music[level_nr] != MUS_UNDEFINED)
16046     PlayMusic(levelset.music[level_nr]);        /* from config file */
16047   else
16048     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16049 }
16050
16051 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16052 {
16053   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16054   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16055   int x = xx - 1 - offset;
16056   int y = yy - 1 - offset;
16057
16058   switch (sample)
16059   {
16060     case SAMPLE_blank:
16061       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16062       break;
16063
16064     case SAMPLE_roll:
16065       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16066       break;
16067
16068     case SAMPLE_stone:
16069       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16070       break;
16071
16072     case SAMPLE_nut:
16073       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16074       break;
16075
16076     case SAMPLE_crack:
16077       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16078       break;
16079
16080     case SAMPLE_bug:
16081       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16082       break;
16083
16084     case SAMPLE_tank:
16085       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16086       break;
16087
16088     case SAMPLE_android_clone:
16089       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16090       break;
16091
16092     case SAMPLE_android_move:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16094       break;
16095
16096     case SAMPLE_spring:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16098       break;
16099
16100     case SAMPLE_slurp:
16101       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16102       break;
16103
16104     case SAMPLE_eater:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16106       break;
16107
16108     case SAMPLE_eater_eat:
16109       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16110       break;
16111
16112     case SAMPLE_alien:
16113       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16114       break;
16115
16116     case SAMPLE_collect:
16117       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16118       break;
16119
16120     case SAMPLE_diamond:
16121       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16122       break;
16123
16124     case SAMPLE_squash:
16125       /* !!! CHECK THIS !!! */
16126 #if 1
16127       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16128 #else
16129       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16130 #endif
16131       break;
16132
16133     case SAMPLE_wonderfall:
16134       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16135       break;
16136
16137     case SAMPLE_drip:
16138       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16139       break;
16140
16141     case SAMPLE_push:
16142       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16143       break;
16144
16145     case SAMPLE_dirt:
16146       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16147       break;
16148
16149     case SAMPLE_acid:
16150       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16151       break;
16152
16153     case SAMPLE_ball:
16154       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16155       break;
16156
16157     case SAMPLE_grow:
16158       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16159       break;
16160
16161     case SAMPLE_wonder:
16162       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16163       break;
16164
16165     case SAMPLE_door:
16166       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16167       break;
16168
16169     case SAMPLE_exit_open:
16170       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16171       break;
16172
16173     case SAMPLE_exit_leave:
16174       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16175       break;
16176
16177     case SAMPLE_dynamite:
16178       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16179       break;
16180
16181     case SAMPLE_tick:
16182       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16183       break;
16184
16185     case SAMPLE_press:
16186       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16187       break;
16188
16189     case SAMPLE_wheel:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16191       break;
16192
16193     case SAMPLE_boom:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16195       break;
16196
16197     case SAMPLE_die:
16198       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16199       break;
16200
16201     case SAMPLE_time:
16202       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16203       break;
16204
16205     default:
16206       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16207       break;
16208   }
16209 }
16210
16211 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16212 {
16213   int element = map_element_SP_to_RND(element_sp);
16214   int action = map_action_SP_to_RND(action_sp);
16215   int offset = (setup.sp_show_border_elements ? 0 : 1);
16216   int x = xx - offset;
16217   int y = yy - offset;
16218
16219 #if 0
16220   printf("::: %d -> %d\n", element_sp, action_sp);
16221 #endif
16222
16223   PlayLevelSoundElementAction(x, y, element, action);
16224 }
16225
16226 #if 0
16227 void ChangeTime(int value)
16228 {
16229   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16230
16231   *time += value;
16232
16233   /* EMC game engine uses value from time counter of RND game engine */
16234   level.native_em_level->lev->time = *time;
16235
16236   DrawGameValue_Time(*time);
16237 }
16238
16239 void RaiseScore(int value)
16240 {
16241   /* EMC game engine and RND game engine have separate score counters */
16242   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16243                 &level.native_em_level->lev->score : &local_player->score);
16244
16245   *score += value;
16246
16247   DrawGameValue_Score(*score);
16248 }
16249 #endif
16250
16251 void RaiseScore(int value)
16252 {
16253   local_player->score += value;
16254
16255 #if 1
16256   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16257
16258   DisplayGameControlValues();
16259 #else
16260   DrawGameValue_Score(local_player->score);
16261 #endif
16262 }
16263
16264 void RaiseScoreElement(int element)
16265 {
16266   switch (element)
16267   {
16268     case EL_EMERALD:
16269     case EL_BD_DIAMOND:
16270     case EL_EMERALD_YELLOW:
16271     case EL_EMERALD_RED:
16272     case EL_EMERALD_PURPLE:
16273     case EL_SP_INFOTRON:
16274       RaiseScore(level.score[SC_EMERALD]);
16275       break;
16276     case EL_DIAMOND:
16277       RaiseScore(level.score[SC_DIAMOND]);
16278       break;
16279     case EL_CRYSTAL:
16280       RaiseScore(level.score[SC_CRYSTAL]);
16281       break;
16282     case EL_PEARL:
16283       RaiseScore(level.score[SC_PEARL]);
16284       break;
16285     case EL_BUG:
16286     case EL_BD_BUTTERFLY:
16287     case EL_SP_ELECTRON:
16288       RaiseScore(level.score[SC_BUG]);
16289       break;
16290     case EL_SPACESHIP:
16291     case EL_BD_FIREFLY:
16292     case EL_SP_SNIKSNAK:
16293       RaiseScore(level.score[SC_SPACESHIP]);
16294       break;
16295     case EL_YAMYAM:
16296     case EL_DARK_YAMYAM:
16297       RaiseScore(level.score[SC_YAMYAM]);
16298       break;
16299     case EL_ROBOT:
16300       RaiseScore(level.score[SC_ROBOT]);
16301       break;
16302     case EL_PACMAN:
16303       RaiseScore(level.score[SC_PACMAN]);
16304       break;
16305     case EL_NUT:
16306       RaiseScore(level.score[SC_NUT]);
16307       break;
16308     case EL_DYNAMITE:
16309     case EL_EM_DYNAMITE:
16310     case EL_SP_DISK_RED:
16311     case EL_DYNABOMB_INCREASE_NUMBER:
16312     case EL_DYNABOMB_INCREASE_SIZE:
16313     case EL_DYNABOMB_INCREASE_POWER:
16314       RaiseScore(level.score[SC_DYNAMITE]);
16315       break;
16316     case EL_SHIELD_NORMAL:
16317     case EL_SHIELD_DEADLY:
16318       RaiseScore(level.score[SC_SHIELD]);
16319       break;
16320     case EL_EXTRA_TIME:
16321       RaiseScore(level.extra_time_score);
16322       break;
16323     case EL_KEY_1:
16324     case EL_KEY_2:
16325     case EL_KEY_3:
16326     case EL_KEY_4:
16327     case EL_EM_KEY_1:
16328     case EL_EM_KEY_2:
16329     case EL_EM_KEY_3:
16330     case EL_EM_KEY_4:
16331     case EL_EMC_KEY_5:
16332     case EL_EMC_KEY_6:
16333     case EL_EMC_KEY_7:
16334     case EL_EMC_KEY_8:
16335     case EL_DC_KEY_WHITE:
16336       RaiseScore(level.score[SC_KEY]);
16337       break;
16338     default:
16339       RaiseScore(element_info[element].collect_score);
16340       break;
16341   }
16342 }
16343
16344 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16345 {
16346   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16347   {
16348 #if defined(NETWORK_AVALIABLE)
16349     if (options.network)
16350       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16351     else
16352 #endif
16353     {
16354       if (quick_quit)
16355       {
16356 #if 1
16357
16358 #if 1
16359         FadeSkipNextFadeIn();
16360 #else
16361         fading = fading_none;
16362 #endif
16363
16364 #else
16365         OpenDoor(DOOR_CLOSE_1);
16366 #endif
16367
16368         game_status = GAME_MODE_MAIN;
16369
16370 #if 1
16371         DrawAndFadeInMainMenu(REDRAW_FIELD);
16372 #else
16373         DrawMainMenu();
16374 #endif
16375       }
16376       else
16377       {
16378 #if 0
16379         FadeOut(REDRAW_FIELD);
16380 #endif
16381
16382         game_status = GAME_MODE_MAIN;
16383
16384         DrawAndFadeInMainMenu(REDRAW_FIELD);
16385       }
16386     }
16387   }
16388   else          /* continue playing the game */
16389   {
16390     if (tape.playing && tape.deactivate_display)
16391       TapeDeactivateDisplayOff(TRUE);
16392
16393     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16394
16395     if (tape.playing && tape.deactivate_display)
16396       TapeDeactivateDisplayOn();
16397   }
16398 }
16399
16400 void RequestQuitGame(boolean ask_if_really_quit)
16401 {
16402   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16403   boolean skip_request = AllPlayersGone || quick_quit;
16404
16405   RequestQuitGameExt(skip_request, quick_quit,
16406                      "Do you really want to quit the game ?");
16407 }
16408
16409
16410 /* ------------------------------------------------------------------------- */
16411 /* random generator functions                                                */
16412 /* ------------------------------------------------------------------------- */
16413
16414 unsigned int InitEngineRandom_RND(long seed)
16415 {
16416   game.num_random_calls = 0;
16417
16418 #if 0
16419   unsigned int rnd_seed = InitEngineRandom(seed);
16420
16421   printf("::: START RND: %d\n", rnd_seed);
16422
16423   return rnd_seed;
16424 #else
16425
16426   return InitEngineRandom(seed);
16427
16428 #endif
16429
16430 }
16431
16432 unsigned int RND(int max)
16433 {
16434   if (max > 0)
16435   {
16436     game.num_random_calls++;
16437
16438     return GetEngineRandom(max);
16439   }
16440
16441   return 0;
16442 }
16443
16444
16445 /* ------------------------------------------------------------------------- */
16446 /* game engine snapshot handling functions                                   */
16447 /* ------------------------------------------------------------------------- */
16448
16449 struct EngineSnapshotInfo
16450 {
16451   /* runtime values for custom element collect score */
16452   int collect_score[NUM_CUSTOM_ELEMENTS];
16453
16454   /* runtime values for group element choice position */
16455   int choice_pos[NUM_GROUP_ELEMENTS];
16456
16457   /* runtime values for belt position animations */
16458   int belt_graphic[4][NUM_BELT_PARTS];
16459   int belt_anim_mode[4][NUM_BELT_PARTS];
16460 };
16461
16462 static struct EngineSnapshotInfo engine_snapshot_rnd;
16463 static char *snapshot_level_identifier = NULL;
16464 static int snapshot_level_nr = -1;
16465
16466 static void SaveEngineSnapshotValues_RND()
16467 {
16468   static int belt_base_active_element[4] =
16469   {
16470     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16471     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16472     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16473     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16474   };
16475   int i, j;
16476
16477   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16478   {
16479     int element = EL_CUSTOM_START + i;
16480
16481     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16482   }
16483
16484   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16485   {
16486     int element = EL_GROUP_START + i;
16487
16488     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16489   }
16490
16491   for (i = 0; i < 4; i++)
16492   {
16493     for (j = 0; j < NUM_BELT_PARTS; j++)
16494     {
16495       int element = belt_base_active_element[i] + j;
16496       int graphic = el2img(element);
16497       int anim_mode = graphic_info[graphic].anim_mode;
16498
16499       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16500       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16501     }
16502   }
16503 }
16504
16505 static void LoadEngineSnapshotValues_RND()
16506 {
16507   unsigned long num_random_calls = game.num_random_calls;
16508   int i, j;
16509
16510   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16511   {
16512     int element = EL_CUSTOM_START + i;
16513
16514     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16515   }
16516
16517   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16518   {
16519     int element = EL_GROUP_START + i;
16520
16521     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16522   }
16523
16524   for (i = 0; i < 4; i++)
16525   {
16526     for (j = 0; j < NUM_BELT_PARTS; j++)
16527     {
16528       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16529       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16530
16531       graphic_info[graphic].anim_mode = anim_mode;
16532     }
16533   }
16534
16535   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16536   {
16537     InitRND(tape.random_seed);
16538     for (i = 0; i < num_random_calls; i++)
16539       RND(1);
16540   }
16541
16542   if (game.num_random_calls != num_random_calls)
16543   {
16544     Error(ERR_INFO, "number of random calls out of sync");
16545     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16546     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16547     Error(ERR_EXIT, "this should not happen -- please debug");
16548   }
16549 }
16550
16551 void SaveEngineSnapshot()
16552 {
16553   /* do not save snapshots from editor */
16554   if (level_editor_test_game)
16555     return;
16556
16557   /* free previous snapshot buffers, if needed */
16558   FreeEngineSnapshotBuffers();
16559
16560   /* copy some special values to a structure better suited for the snapshot */
16561
16562   SaveEngineSnapshotValues_RND();
16563   SaveEngineSnapshotValues_EM();
16564   SaveEngineSnapshotValues_SP();
16565
16566   /* save values stored in special snapshot structure */
16567
16568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16571
16572   /* save further RND engine values */
16573
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16577
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16582
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16588
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16592
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16594
16595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16596
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16599
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16618
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16621
16622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16625
16626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16628
16629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16634
16635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16637
16638   /* save level identification information */
16639
16640   setString(&snapshot_level_identifier, leveldir_current->identifier);
16641   snapshot_level_nr = level_nr;
16642
16643 #if 0
16644   ListNode *node = engine_snapshot_list_rnd;
16645   int num_bytes = 0;
16646
16647   while (node != NULL)
16648   {
16649     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16650
16651     node = node->next;
16652   }
16653
16654   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16655 #endif
16656 }
16657
16658 void LoadEngineSnapshot()
16659 {
16660   /* restore generically stored snapshot buffers */
16661
16662   LoadEngineSnapshotBuffers();
16663
16664   /* restore special values from snapshot structure */
16665
16666   LoadEngineSnapshotValues_RND();
16667   LoadEngineSnapshotValues_EM();
16668   LoadEngineSnapshotValues_SP();
16669 }
16670
16671 boolean CheckEngineSnapshot()
16672 {
16673   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16674           snapshot_level_nr == level_nr);
16675 }
16676
16677
16678 /* ---------- new game button stuff ---------------------------------------- */
16679
16680 /* graphic position values for game buttons */
16681 #define GAME_BUTTON_XSIZE       30
16682 #define GAME_BUTTON_YSIZE       30
16683 #define GAME_BUTTON_XPOS        5
16684 #define GAME_BUTTON_YPOS        215
16685 #define SOUND_BUTTON_XPOS       5
16686 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16687
16688 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16689 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16690 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16691 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16692 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16693 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16694
16695 static struct
16696 {
16697   int *x, *y;
16698   int gd_x, gd_y;
16699   int gadget_id;
16700   char *infotext;
16701 } gamebutton_info[NUM_GAME_BUTTONS] =
16702 {
16703 #if 1
16704   {
16705     &game.button.stop.x,        &game.button.stop.y,
16706     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16707     GAME_CTRL_ID_STOP,
16708     "stop game"
16709   },
16710   {
16711     &game.button.pause.x,       &game.button.pause.y,
16712     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16713     GAME_CTRL_ID_PAUSE,
16714     "pause game"
16715   },
16716   {
16717     &game.button.play.x,        &game.button.play.y,
16718     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16719     GAME_CTRL_ID_PLAY,
16720     "play game"
16721   },
16722   {
16723     &game.button.sound_music.x, &game.button.sound_music.y,
16724     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16725     SOUND_CTRL_ID_MUSIC,
16726     "background music on/off"
16727   },
16728   {
16729     &game.button.sound_loops.x, &game.button.sound_loops.y,
16730     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16731     SOUND_CTRL_ID_LOOPS,
16732     "sound loops on/off"
16733   },
16734   {
16735     &game.button.sound_simple.x,&game.button.sound_simple.y,
16736     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16737     SOUND_CTRL_ID_SIMPLE,
16738     "normal sounds on/off"
16739   }
16740 #else
16741   {
16742     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16743     GAME_CTRL_ID_STOP,
16744     "stop game"
16745   },
16746   {
16747     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16748     GAME_CTRL_ID_PAUSE,
16749     "pause game"
16750   },
16751   {
16752     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16753     GAME_CTRL_ID_PLAY,
16754     "play game"
16755   },
16756   {
16757     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16758     SOUND_CTRL_ID_MUSIC,
16759     "background music on/off"
16760   },
16761   {
16762     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16763     SOUND_CTRL_ID_LOOPS,
16764     "sound loops on/off"
16765   },
16766   {
16767     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16768     SOUND_CTRL_ID_SIMPLE,
16769     "normal sounds on/off"
16770   }
16771 #endif
16772 };
16773
16774 void CreateGameButtons()
16775 {
16776   int i;
16777
16778   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16779   {
16780     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16781     struct GadgetInfo *gi;
16782     int button_type;
16783     boolean checked;
16784     unsigned long event_mask;
16785     int x, y;
16786     int gd_xoffset, gd_yoffset;
16787     int gd_x1, gd_x2, gd_y1, gd_y2;
16788     int id = i;
16789
16790     x = DX + *gamebutton_info[i].x;
16791     y = DY + *gamebutton_info[i].y;
16792     gd_xoffset = gamebutton_info[i].gd_x;
16793     gd_yoffset = gamebutton_info[i].gd_y;
16794     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16795     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16796
16797     if (id == GAME_CTRL_ID_STOP ||
16798         id == GAME_CTRL_ID_PAUSE ||
16799         id == GAME_CTRL_ID_PLAY)
16800     {
16801       button_type = GD_TYPE_NORMAL_BUTTON;
16802       checked = FALSE;
16803       event_mask = GD_EVENT_RELEASED;
16804       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16805       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16806     }
16807     else
16808     {
16809       button_type = GD_TYPE_CHECK_BUTTON;
16810       checked =
16811         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16812          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16813          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16814       event_mask = GD_EVENT_PRESSED;
16815       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16816       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16817     }
16818
16819     gi = CreateGadget(GDI_CUSTOM_ID, id,
16820                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16821 #if 1
16822                       GDI_X, x,
16823                       GDI_Y, y,
16824 #else
16825                       GDI_X, DX + gd_xoffset,
16826                       GDI_Y, DY + gd_yoffset,
16827 #endif
16828                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16829                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16830                       GDI_TYPE, button_type,
16831                       GDI_STATE, GD_BUTTON_UNPRESSED,
16832                       GDI_CHECKED, checked,
16833                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16834                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16835                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16836                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16837                       GDI_DIRECT_DRAW, FALSE,
16838                       GDI_EVENT_MASK, event_mask,
16839                       GDI_CALLBACK_ACTION, HandleGameButtons,
16840                       GDI_END);
16841
16842     if (gi == NULL)
16843       Error(ERR_EXIT, "cannot create gadget");
16844
16845     game_gadget[id] = gi;
16846   }
16847 }
16848
16849 void FreeGameButtons()
16850 {
16851   int i;
16852
16853   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16854     FreeGadget(game_gadget[i]);
16855 }
16856
16857 static void MapGameButtons()
16858 {
16859   int i;
16860
16861   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862     MapGadget(game_gadget[i]);
16863 }
16864
16865 void UnmapGameButtons()
16866 {
16867   int i;
16868
16869   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870     UnmapGadget(game_gadget[i]);
16871 }
16872
16873 void RedrawGameButtons()
16874 {
16875   int i;
16876
16877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878     RedrawGadget(game_gadget[i]);
16879 }
16880
16881 static void HandleGameButtonsExt(int id)
16882 {
16883   if (game_status != GAME_MODE_PLAYING)
16884     return;
16885
16886   switch (id)
16887   {
16888     case GAME_CTRL_ID_STOP:
16889       if (tape.playing)
16890         TapeStop();
16891       else
16892         RequestQuitGame(TRUE);
16893       break;
16894
16895     case GAME_CTRL_ID_PAUSE:
16896       if (options.network)
16897       {
16898 #if defined(NETWORK_AVALIABLE)
16899         if (tape.pausing)
16900           SendToServer_ContinuePlaying();
16901         else
16902           SendToServer_PausePlaying();
16903 #endif
16904       }
16905       else
16906         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16907       break;
16908
16909     case GAME_CTRL_ID_PLAY:
16910       if (tape.pausing)
16911       {
16912 #if defined(NETWORK_AVALIABLE)
16913         if (options.network)
16914           SendToServer_ContinuePlaying();
16915         else
16916 #endif
16917         {
16918           tape.pausing = FALSE;
16919           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16920         }
16921       }
16922       break;
16923
16924     case SOUND_CTRL_ID_MUSIC:
16925       if (setup.sound_music)
16926       { 
16927         setup.sound_music = FALSE;
16928
16929         FadeMusic();
16930       }
16931       else if (audio.music_available)
16932       { 
16933         setup.sound = setup.sound_music = TRUE;
16934
16935         SetAudioMode(setup.sound);
16936
16937         PlayLevelMusic();
16938       }
16939       break;
16940
16941     case SOUND_CTRL_ID_LOOPS:
16942       if (setup.sound_loops)
16943         setup.sound_loops = FALSE;
16944       else if (audio.loops_available)
16945       {
16946         setup.sound = setup.sound_loops = TRUE;
16947
16948         SetAudioMode(setup.sound);
16949       }
16950       break;
16951
16952     case SOUND_CTRL_ID_SIMPLE:
16953       if (setup.sound_simple)
16954         setup.sound_simple = FALSE;
16955       else if (audio.sound_available)
16956       {
16957         setup.sound = setup.sound_simple = TRUE;
16958
16959         SetAudioMode(setup.sound);
16960       }
16961       break;
16962
16963     default:
16964       break;
16965   }
16966 }
16967
16968 static void HandleGameButtons(struct GadgetInfo *gi)
16969 {
16970   HandleGameButtonsExt(gi->custom_id);
16971 }
16972
16973 void HandleSoundButtonKeys(Key key)
16974 {
16975 #if 1
16976   if (key == setup.shortcut.sound_simple)
16977     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16978   else if (key == setup.shortcut.sound_loops)
16979     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16980   else if (key == setup.shortcut.sound_music)
16981     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16982 #else
16983   if (key == setup.shortcut.sound_simple)
16984     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16985   else if (key == setup.shortcut.sound_loops)
16986     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16987   else if (key == setup.shortcut.sound_music)
16988     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16989 #endif
16990 }