rnd-20091012-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.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active &&
4232           !tape.player_participates[i])
4233       {
4234         struct PlayerInfo *player = &stored_player[i];
4235         int jx = player->jx, jy = player->jy;
4236
4237         player->active = FALSE;
4238         StorePlayer[jx][jy] = 0;
4239         Feld[jx][jy] = EL_EMPTY;
4240       }
4241     }
4242 #endif
4243   }
4244   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4245   {
4246     /* when in single player mode, eliminate all but the first active player */
4247
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249     {
4250       if (stored_player[i].active)
4251       {
4252         for (j = i + 1; j < MAX_PLAYERS; j++)
4253         {
4254           if (stored_player[j].active)
4255           {
4256             struct PlayerInfo *player = &stored_player[j];
4257             int jx = player->jx, jy = player->jy;
4258
4259             player->active = FALSE;
4260             player->present = FALSE;
4261
4262             StorePlayer[jx][jy] = 0;
4263             Feld[jx][jy] = EL_EMPTY;
4264           }
4265         }
4266       }
4267     }
4268   }
4269
4270   /* when recording the game, store which players take part in the game */
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284   if (options.debug)
4285   {
4286     for (i = 0; i < MAX_PLAYERS; i++)
4287     {
4288       struct PlayerInfo *player = &stored_player[i];
4289
4290       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4291              i+1,
4292              player->present,
4293              player->connected,
4294              player->active);
4295       if (local_player == player)
4296         printf("Player  %d is local player.\n", i+1);
4297     }
4298   }
4299
4300   if (BorderElement == EL_EMPTY)
4301   {
4302     SBX_Left = 0;
4303     SBX_Right = lev_fieldx - SCR_FIELDX;
4304     SBY_Upper = 0;
4305     SBY_Lower = lev_fieldy - SCR_FIELDY;
4306   }
4307   else
4308   {
4309     SBX_Left = -1;
4310     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311     SBY_Upper = -1;
4312     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4313   }
4314
4315   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320
4321   /* if local player not found, look for custom element that might create
4322      the player (make some assumptions about the right custom element) */
4323   if (!local_player->present)
4324   {
4325     int start_x = 0, start_y = 0;
4326     int found_rating = 0;
4327     int found_element = EL_UNDEFINED;
4328     int player_nr = local_player->index_nr;
4329
4330     SCAN_PLAYFIELD(x, y)
4331     {
4332       int element = Feld[x][y];
4333       int content;
4334       int xx, yy;
4335       boolean is_player;
4336
4337       if (level.use_start_element[player_nr] &&
4338           level.start_element[player_nr] == element &&
4339           found_rating < 4)
4340       {
4341         start_x = x;
4342         start_y = y;
4343
4344         found_rating = 4;
4345         found_element = element;
4346       }
4347
4348       if (!IS_CUSTOM_ELEMENT(element))
4349         continue;
4350
4351       if (CAN_CHANGE(element))
4352       {
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as single target */
4356           content = element_info[element].change_page[i].target_element;
4357           is_player = ELEM_IS_PLAYER(content);
4358
4359           if (is_player && (found_rating < 3 ||
4360                             (found_rating == 3 && element < found_element)))
4361           {
4362             start_x = x;
4363             start_y = y;
4364
4365             found_rating = 3;
4366             found_element = element;
4367           }
4368         }
4369       }
4370
4371       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372       {
4373         /* check for player created from custom element as explosion content */
4374         content = element_info[element].content.e[xx][yy];
4375         is_player = ELEM_IS_PLAYER(content);
4376
4377         if (is_player && (found_rating < 2 ||
4378                           (found_rating == 2 && element < found_element)))
4379         {
4380           start_x = x + xx - 1;
4381           start_y = y + yy - 1;
4382
4383           found_rating = 2;
4384           found_element = element;
4385         }
4386
4387         if (!CAN_CHANGE(element))
4388           continue;
4389
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           /* check for player created from custom element as extended target */
4393           content =
4394             element_info[element].change_page[i].target_content.e[xx][yy];
4395
4396           is_player = ELEM_IS_PLAYER(content);
4397
4398           if (is_player && (found_rating < 1 ||
4399                             (found_rating == 1 && element < found_element)))
4400           {
4401             start_x = x + xx - 1;
4402             start_y = y + yy - 1;
4403
4404             found_rating = 1;
4405             found_element = element;
4406           }
4407         }
4408       }
4409     }
4410
4411     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4412                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4413                 start_x - MIDPOSX);
4414
4415     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4417                 start_y - MIDPOSY);
4418   }
4419   else
4420   {
4421     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4422                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423                 local_player->jx - MIDPOSX);
4424
4425     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427                 local_player->jy - MIDPOSY);
4428   }
4429
4430 #if 0
4431   /* do not use PLAYING mask for fading out from main screen */
4432   game_status = GAME_MODE_MAIN;
4433 #endif
4434
4435   StopAnimation();
4436
4437   if (!game.restart_level)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440 #if 1
4441   if (level_editor_test_game)
4442     FadeSkipNextFadeIn();
4443   else
4444     FadeSetEnterScreen();
4445 #else
4446   if (level_editor_test_game)
4447     fading = fading_none;
4448   else
4449     fading = menu.destination;
4450 #endif
4451
4452 #if 1
4453   FadeOut(REDRAW_FIELD);
4454 #else
4455   if (do_fading)
4456     FadeOut(REDRAW_FIELD);
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4472   {
4473     InitGameEngine_SP();
4474
4475     /* blit playfield from scroll buffer to normal back buffer for fading in */
4476     BlitScreenToBitmap_SP(backbuffer);
4477   }
4478   else
4479   {
4480     DrawLevel();
4481     DrawAllPlayers();
4482
4483     /* after drawing the level, correct some elements */
4484     if (game.timegate_time_left == 0)
4485       CloseAllOpenTimegates();
4486
4487     /* blit playfield from scroll buffer to normal back buffer for fading in */
4488     if (setup.soft_scrolling)
4489       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4490
4491     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4492   }
4493   /* !!! FIX THIS (END) !!! */
4494
4495 #if 1
4496   FadeIn(REDRAW_FIELD);
4497 #else
4498   if (do_fading)
4499     FadeIn(REDRAW_FIELD);
4500
4501   BackToFront();
4502 #endif
4503
4504   if (!game.restart_level)
4505   {
4506     /* copy default game door content to main double buffer */
4507     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4509   }
4510
4511   SetPanelBackground();
4512   SetDrawBackgroundMask(REDRAW_DOOR_1);
4513
4514 #if 1
4515   UpdateAndDisplayGameControlValues();
4516 #else
4517   UpdateGameDoorValues();
4518   DrawGameDoorValues();
4519 #endif
4520
4521   if (!game.restart_level)
4522   {
4523     UnmapGameButtons();
4524     UnmapTapeButtons();
4525     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4528     MapGameButtons();
4529     MapTapeButtons();
4530
4531     /* copy actual game door content to door double buffer for OpenDoor() */
4532     BlitBitmap(drawto, bitmap_db_door,
4533                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4534
4535     OpenDoor(DOOR_OPEN_ALL);
4536
4537     PlaySound(SND_GAME_STARTING);
4538
4539     if (setup.sound_music)
4540       PlayLevelMusic();
4541
4542     KeyboardAutoRepeatOffUnlessAutoplay();
4543
4544     if (options.debug)
4545     {
4546       for (i = 0; i < MAX_PLAYERS; i++)
4547         printf("Player %d %sactive.\n",
4548                i + 1, (stored_player[i].active ? "" : "not "));
4549     }
4550   }
4551
4552 #if 1
4553   UnmapAllGadgets();
4554
4555   MapGameButtons();
4556   MapTapeButtons();
4557 #endif
4558
4559   game.restart_level = FALSE;
4560 }
4561
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4563 {
4564   /* this is used for non-R'n'D game engines to update certain engine values */
4565
4566   /* needed to determine if sounds are played within the visible screen area */
4567   scroll_x = actual_scroll_x;
4568   scroll_y = actual_scroll_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Feld[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Feld[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Feld[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Feld[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Feld[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Feld[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Feld[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Feld[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     default:
4655       if (IS_CUSTOM_ELEMENT(element))
4656       {
4657         struct ElementInfo *ei = &element_info[element];
4658         int move_direction_initial = ei->move_direction_initial;
4659         int move_pattern = ei->move_pattern;
4660
4661         if (move_direction_initial == MV_START_PREVIOUS)
4662         {
4663           if (MovDir[x][y] != MV_NONE)
4664             return;
4665
4666           move_direction_initial = MV_START_AUTOMATIC;
4667         }
4668
4669         if (move_direction_initial == MV_START_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_direction_initial & MV_ANY_DIRECTION)
4672           MovDir[x][y] = move_direction_initial;
4673         else if (move_pattern == MV_ALL_DIRECTIONS ||
4674                  move_pattern == MV_TURNING_LEFT ||
4675                  move_pattern == MV_TURNING_RIGHT ||
4676                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4677                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4678                  move_pattern == MV_TURNING_RANDOM)
4679           MovDir[x][y] = 1 << RND(4);
4680         else if (move_pattern == MV_HORIZONTAL)
4681           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682         else if (move_pattern == MV_VERTICAL)
4683           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684         else if (move_pattern & MV_ANY_DIRECTION)
4685           MovDir[x][y] = element_info[element].move_pattern;
4686         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687                  move_pattern == MV_ALONG_RIGHT_SIDE)
4688         {
4689           /* use random direction as default start direction */
4690           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691             MovDir[x][y] = 1 << RND(4);
4692
4693           for (i = 0; i < NUM_DIRECTIONS; i++)
4694           {
4695             int x1 = x + xy[i][0];
4696             int y1 = y + xy[i][1];
4697
4698             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4699             {
4700               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701                 MovDir[x][y] = direction[0][i];
4702               else
4703                 MovDir[x][y] = direction[1][i];
4704
4705               break;
4706             }
4707           }
4708         }                
4709       }
4710       else
4711       {
4712         MovDir[x][y] = 1 << RND(4);
4713
4714         if (element != EL_BUG &&
4715             element != EL_SPACESHIP &&
4716             element != EL_BD_BUTTERFLY &&
4717             element != EL_BD_FIREFLY)
4718           break;
4719
4720         for (i = 0; i < NUM_DIRECTIONS; i++)
4721         {
4722           int x1 = x + xy[i][0];
4723           int y1 = y + xy[i][1];
4724
4725           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4726           {
4727             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4728             {
4729               MovDir[x][y] = direction[0][i];
4730               break;
4731             }
4732             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4734             {
4735               MovDir[x][y] = direction[1][i];
4736               break;
4737             }
4738           }
4739         }
4740       }
4741       break;
4742   }
4743
4744   GfxDir[x][y] = MovDir[x][y];
4745 }
4746
4747 void InitAmoebaNr(int x, int y)
4748 {
4749   int i;
4750   int group_nr = AmoebeNachbarNr(x, y);
4751
4752   if (group_nr == 0)
4753   {
4754     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4755     {
4756       if (AmoebaCnt[i] == 0)
4757       {
4758         group_nr = i;
4759         break;
4760       }
4761     }
4762   }
4763
4764   AmoebaNr[x][y] = group_nr;
4765   AmoebaCnt[group_nr]++;
4766   AmoebaCnt2[group_nr]++;
4767 }
4768
4769 static void PlayerWins(struct PlayerInfo *player)
4770 {
4771   player->LevelSolved = TRUE;
4772   player->GameOver = TRUE;
4773
4774   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775                          level.native_em_level->lev->score : player->score);
4776
4777   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778   player->LevelSolved_CountingScore = player->score_final;
4779 }
4780
4781 void GameWon()
4782 {
4783   static int time, time_final;
4784   static int score, score_final;
4785   static int game_over_delay_1 = 0;
4786   static int game_over_delay_2 = 0;
4787   int game_over_delay_value_1 = 50;
4788   int game_over_delay_value_2 = 50;
4789
4790   if (!local_player->LevelSolved_GameWon)
4791   {
4792     int i;
4793
4794     /* do not start end game actions before the player stops moving (to exit) */
4795     if (local_player->MovPos)
4796       return;
4797
4798     local_player->LevelSolved_GameWon = TRUE;
4799     local_player->LevelSolved_SaveTape = tape.recording;
4800     local_player->LevelSolved_SaveScore = !tape.playing;
4801
4802     if (tape.auto_play)         /* tape might already be stopped here */
4803       tape.auto_play_level_solved = TRUE;
4804
4805 #if 1
4806     TapeStop();
4807 #endif
4808
4809     game_over_delay_1 = game_over_delay_value_1;
4810     game_over_delay_2 = game_over_delay_value_2;
4811
4812     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813     score = score_final = local_player->score_final;
4814
4815     if (TimeLeft > 0)
4816     {
4817       time_final = 0;
4818       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4819     }
4820     else if (level.time == 0 && TimePlayed < 999)
4821     {
4822       time_final = 999;
4823       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4824     }
4825
4826     local_player->score_final = score_final;
4827
4828     if (level_editor_test_game)
4829     {
4830       time = time_final;
4831       score = score_final;
4832
4833 #if 1
4834       local_player->LevelSolved_CountingTime = time;
4835       local_player->LevelSolved_CountingScore = score;
4836
4837       game_panel_controls[GAME_PANEL_TIME].value = time;
4838       game_panel_controls[GAME_PANEL_SCORE].value = score;
4839
4840       DisplayGameControlValues();
4841 #else
4842       DrawGameValue_Time(time);
4843       DrawGameValue_Score(score);
4844 #endif
4845     }
4846
4847     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4848     {
4849       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4850       {
4851         /* close exit door after last player */
4852         if ((AllPlayersGone &&
4853              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4858         {
4859           int element = Feld[ExitX][ExitY];
4860
4861 #if 0
4862           if (element == EL_EM_EXIT_OPEN ||
4863               element == EL_EM_STEEL_EXIT_OPEN)
4864           {
4865             Bang(ExitX, ExitY);
4866           }
4867           else
4868 #endif
4869           {
4870             Feld[ExitX][ExitY] =
4871               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4872                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4873                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4874                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4875                EL_EM_STEEL_EXIT_CLOSING);
4876
4877             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4878           }
4879         }
4880
4881         /* player disappears */
4882         DrawLevelField(ExitX, ExitY);
4883       }
4884
4885       for (i = 0; i < MAX_PLAYERS; i++)
4886       {
4887         struct PlayerInfo *player = &stored_player[i];
4888
4889         if (player->present)
4890         {
4891           RemovePlayer(player);
4892
4893           /* player disappears */
4894           DrawLevelField(player->jx, player->jy);
4895         }
4896       }
4897     }
4898
4899     PlaySound(SND_GAME_WINNING);
4900   }
4901
4902   if (game_over_delay_1 > 0)
4903   {
4904     game_over_delay_1--;
4905
4906     return;
4907   }
4908
4909   if (time != time_final)
4910   {
4911     int time_to_go = ABS(time_final - time);
4912     int time_count_dir = (time < time_final ? +1 : -1);
4913     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4914
4915     time  += time_count_steps * time_count_dir;
4916     score += time_count_steps * level.score[SC_TIME_BONUS];
4917
4918 #if 1
4919     local_player->LevelSolved_CountingTime = time;
4920     local_player->LevelSolved_CountingScore = score;
4921
4922     game_panel_controls[GAME_PANEL_TIME].value = time;
4923     game_panel_controls[GAME_PANEL_SCORE].value = score;
4924
4925     DisplayGameControlValues();
4926 #else
4927     DrawGameValue_Time(time);
4928     DrawGameValue_Score(score);
4929 #endif
4930
4931     if (time == time_final)
4932       StopSound(SND_GAME_LEVELTIME_BONUS);
4933     else if (setup.sound_loops)
4934       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4935     else
4936       PlaySound(SND_GAME_LEVELTIME_BONUS);
4937
4938     return;
4939   }
4940
4941   local_player->LevelSolved_PanelOff = TRUE;
4942
4943   if (game_over_delay_2 > 0)
4944   {
4945     game_over_delay_2--;
4946
4947     return;
4948   }
4949
4950 #if 1
4951   GameEnd();
4952 #endif
4953 }
4954
4955 void GameEnd()
4956 {
4957   int hi_pos;
4958   boolean raise_level = FALSE;
4959
4960   local_player->LevelSolved_GameEnd = TRUE;
4961
4962   CloseDoor(DOOR_CLOSE_1);
4963
4964   if (local_player->LevelSolved_SaveTape)
4965   {
4966 #if 0
4967     TapeStop();
4968 #endif
4969
4970 #if 1
4971     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4972 #else
4973     SaveTape(tape.level_nr);            /* ask to save tape */
4974 #endif
4975   }
4976
4977   if (level_editor_test_game)
4978   {
4979     game_status = GAME_MODE_MAIN;
4980
4981 #if 1
4982     DrawAndFadeInMainMenu(REDRAW_FIELD);
4983 #else
4984     DrawMainMenu();
4985 #endif
4986
4987     return;
4988   }
4989
4990   if (!local_player->LevelSolved_SaveScore)
4991   {
4992 #if 1
4993     FadeOut(REDRAW_FIELD);
4994 #endif
4995
4996     game_status = GAME_MODE_MAIN;
4997
4998     DrawAndFadeInMainMenu(REDRAW_FIELD);
4999
5000     return;
5001   }
5002
5003   if (level_nr == leveldir_current->handicap_level)
5004   {
5005     leveldir_current->handicap_level++;
5006     SaveLevelSetup_SeriesInfo();
5007   }
5008
5009   if (level_nr < leveldir_current->last_level)
5010     raise_level = TRUE;                 /* advance to next level */
5011
5012   if ((hi_pos = NewHiScore()) >= 0) 
5013   {
5014     game_status = GAME_MODE_SCORES;
5015
5016     DrawHallOfFame(hi_pos);
5017
5018     if (raise_level)
5019     {
5020       level_nr++;
5021       TapeErase();
5022     }
5023   }
5024   else
5025   {
5026 #if 1
5027     FadeOut(REDRAW_FIELD);
5028 #endif
5029
5030     game_status = GAME_MODE_MAIN;
5031
5032     if (raise_level)
5033     {
5034       level_nr++;
5035       TapeErase();
5036     }
5037
5038     DrawAndFadeInMainMenu(REDRAW_FIELD);
5039   }
5040 }
5041
5042 int NewHiScore()
5043 {
5044   int k, l;
5045   int position = -1;
5046
5047   LoadScore(level_nr);
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5051     return -1;
5052
5053   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5054   {
5055     if (local_player->score_final > highscore[k].Score)
5056     {
5057       /* player has made it to the hall of fame */
5058
5059       if (k < MAX_SCORE_ENTRIES - 1)
5060       {
5061         int m = MAX_SCORE_ENTRIES - 1;
5062
5063 #ifdef ONE_PER_NAME
5064         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065           if (strEqual(setup.player_name, highscore[l].Name))
5066             m = l;
5067         if (m == k)     /* player's new highscore overwrites his old one */
5068           goto put_into_list;
5069 #endif
5070
5071         for (l = m; l > k; l--)
5072         {
5073           strcpy(highscore[l].Name, highscore[l - 1].Name);
5074           highscore[l].Score = highscore[l - 1].Score;
5075         }
5076       }
5077
5078 #ifdef ONE_PER_NAME
5079       put_into_list:
5080 #endif
5081       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083       highscore[k].Score = local_player->score_final; 
5084       position = k;
5085       break;
5086     }
5087
5088 #ifdef ONE_PER_NAME
5089     else if (!strncmp(setup.player_name, highscore[k].Name,
5090                       MAX_PLAYER_NAME_LEN))
5091       break;    /* player already there with a higher score */
5092 #endif
5093
5094   }
5095
5096   if (position >= 0) 
5097     SaveScore(level_nr);
5098
5099   return position;
5100 }
5101
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5103 {
5104   int element = Feld[x][y];
5105   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5107   int horiz_move = (dx != 0);
5108   int sign = (horiz_move ? dx : dy);
5109   int step = sign * element_info[element].move_stepsize;
5110
5111   /* special values for move stepsize for spring and things on conveyor belt */
5112   if (horiz_move)
5113   {
5114     if (CAN_FALL(element) &&
5115         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117     else if (element == EL_SPRING)
5118       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5119   }
5120
5121   return step;
5122 }
5123
5124 inline static int getElementMoveStepsize(int x, int y)
5125 {
5126   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5127 }
5128
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5130 {
5131   if (player->GfxAction != action || player->GfxDir != dir)
5132   {
5133 #if 0
5134     printf("Player frame reset! (%d => %d, %d => %d)\n",
5135            player->GfxAction, action, player->GfxDir, dir);
5136 #endif
5137
5138     player->GfxAction = action;
5139     player->GfxDir = dir;
5140     player->Frame = 0;
5141     player->StepFrame = 0;
5142   }
5143 }
5144
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5147 {
5148   int element = Feld[x][y];
5149   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150   int last_gfx_frame = GfxFrame[x][y];
5151
5152   if (graphic_info[graphic].anim_global_sync)
5153     GfxFrame[x][y] = FrameCounter;
5154   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155     GfxFrame[x][y] = CustomValue[x][y];
5156   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157     GfxFrame[x][y] = element_info[element].collect_score;
5158   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159     GfxFrame[x][y] = ChangeDelay[x][y];
5160
5161   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162     DrawLevelGraphicAnimation(x, y, graphic);
5163 }
5164 #endif
5165
5166 static void ResetGfxAnimation(int x, int y)
5167 {
5168   GfxAction[x][y] = ACTION_DEFAULT;
5169   GfxDir[x][y] = MovDir[x][y];
5170   GfxFrame[x][y] = 0;
5171
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173   ResetGfxFrame(x, y, FALSE);
5174 #endif
5175 }
5176
5177 static void ResetRandomAnimationValue(int x, int y)
5178 {
5179   GfxRandom[x][y] = INIT_GFX_RANDOM();
5180 }
5181
5182 void InitMovingField(int x, int y, int direction)
5183 {
5184   int element = Feld[x][y];
5185   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5187   int newx = x + dx;
5188   int newy = y + dy;
5189   boolean is_moving_before, is_moving_after;
5190 #if 0
5191   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5192 #endif
5193
5194   /* check if element was/is moving or being moved before/after mode change */
5195 #if 1
5196 #if 1
5197   is_moving_before = (WasJustMoving[x][y] != 0);
5198 #else
5199   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200   is_moving_before = WasJustMoving[x][y];
5201 #endif
5202 #else
5203   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5204 #endif
5205   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5206
5207   /* reset animation only for moving elements which change direction of moving
5208      or which just started or stopped moving
5209      (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5211 #if 1
5212   if (is_moving_before != is_moving_after ||
5213       direction != MovDir[x][y])
5214     ResetGfxAnimation(x, y);
5215 #else
5216   if ((is_moving_before || is_moving_after) && !continues_moving)
5217     ResetGfxAnimation(x, y);
5218 #endif
5219 #else
5220   if (!continues_moving)
5221     ResetGfxAnimation(x, y);
5222 #endif
5223
5224   MovDir[x][y] = direction;
5225   GfxDir[x][y] = direction;
5226
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229                      direction == MV_DOWN && CAN_FALL(element) ?
5230                      ACTION_FALLING : ACTION_MOVING);
5231 #else
5232   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233                      ACTION_FALLING : ACTION_MOVING);
5234 #endif
5235
5236   /* this is needed for CEs with property "can move" / "not moving" */
5237
5238   if (is_moving_after)
5239   {
5240     if (Feld[newx][newy] == EL_EMPTY)
5241       Feld[newx][newy] = EL_BLOCKED;
5242
5243     MovDir[newx][newy] = MovDir[x][y];
5244
5245 #if USE_NEW_CUSTOM_VALUE
5246     CustomValue[newx][newy] = CustomValue[x][y];
5247 #endif
5248
5249     GfxFrame[newx][newy] = GfxFrame[x][y];
5250     GfxRandom[newx][newy] = GfxRandom[x][y];
5251     GfxAction[newx][newy] = GfxAction[x][y];
5252     GfxDir[newx][newy] = GfxDir[x][y];
5253   }
5254 }
5255
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5257 {
5258   int direction = MovDir[x][y];
5259   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5261
5262   *goes_to_x = newx;
5263   *goes_to_y = newy;
5264 }
5265
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5267 {
5268   int oldx = x, oldy = y;
5269   int direction = MovDir[x][y];
5270
5271   if (direction == MV_LEFT)
5272     oldx++;
5273   else if (direction == MV_RIGHT)
5274     oldx--;
5275   else if (direction == MV_UP)
5276     oldy++;
5277   else if (direction == MV_DOWN)
5278     oldy--;
5279
5280   *comes_from_x = oldx;
5281   *comes_from_y = oldy;
5282 }
5283
5284 int MovingOrBlocked2Element(int x, int y)
5285 {
5286   int element = Feld[x][y];
5287
5288   if (element == EL_BLOCKED)
5289   {
5290     int oldx, oldy;
5291
5292     Blocked2Moving(x, y, &oldx, &oldy);
5293     return Feld[oldx][oldy];
5294   }
5295   else
5296     return element;
5297 }
5298
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5300 {
5301   /* like MovingOrBlocked2Element(), but if element is moving
5302      and (x,y) is the field the moving element is just leaving,
5303      return EL_BLOCKED instead of the element value */
5304   int element = Feld[x][y];
5305
5306   if (IS_MOVING(x, y))
5307   {
5308     if (element == EL_BLOCKED)
5309     {
5310       int oldx, oldy;
5311
5312       Blocked2Moving(x, y, &oldx, &oldy);
5313       return Feld[oldx][oldy];
5314     }
5315     else
5316       return EL_BLOCKED;
5317   }
5318   else
5319     return element;
5320 }
5321
5322 static void RemoveField(int x, int y)
5323 {
5324   Feld[x][y] = EL_EMPTY;
5325
5326   MovPos[x][y] = 0;
5327   MovDir[x][y] = 0;
5328   MovDelay[x][y] = 0;
5329
5330 #if USE_NEW_CUSTOM_VALUE
5331   CustomValue[x][y] = 0;
5332 #endif
5333
5334   AmoebaNr[x][y] = 0;
5335   ChangeDelay[x][y] = 0;
5336   ChangePage[x][y] = -1;
5337   Pushed[x][y] = FALSE;
5338
5339 #if 0
5340   ExplodeField[x][y] = EX_TYPE_NONE;
5341 #endif
5342
5343   GfxElement[x][y] = EL_UNDEFINED;
5344   GfxAction[x][y] = ACTION_DEFAULT;
5345   GfxDir[x][y] = MV_NONE;
5346 #if 0
5347   /* !!! this would prevent the removed tile from being redrawn !!! */
5348   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5349 #endif
5350 }
5351
5352 void RemoveMovingField(int x, int y)
5353 {
5354   int oldx = x, oldy = y, newx = x, newy = y;
5355   int element = Feld[x][y];
5356   int next_element = EL_UNDEFINED;
5357
5358   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5359     return;
5360
5361   if (IS_MOVING(x, y))
5362   {
5363     Moving2Blocked(x, y, &newx, &newy);
5364
5365     if (Feld[newx][newy] != EL_BLOCKED)
5366     {
5367       /* element is moving, but target field is not free (blocked), but
5368          already occupied by something different (example: acid pool);
5369          in this case, only remove the moving field, but not the target */
5370
5371       RemoveField(oldx, oldy);
5372
5373       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5374
5375       TEST_DrawLevelField(oldx, oldy);
5376
5377       return;
5378     }
5379   }
5380   else if (element == EL_BLOCKED)
5381   {
5382     Blocked2Moving(x, y, &oldx, &oldy);
5383     if (!IS_MOVING(oldx, oldy))
5384       return;
5385   }
5386
5387   if (element == EL_BLOCKED &&
5388       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394     next_element = get_next_element(Feld[oldx][oldy]);
5395
5396   RemoveField(oldx, oldy);
5397   RemoveField(newx, newy);
5398
5399   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401   if (next_element != EL_UNDEFINED)
5402     Feld[oldx][oldy] = next_element;
5403
5404   TEST_DrawLevelField(oldx, oldy);
5405   TEST_DrawLevelField(newx, newy);
5406 }
5407
5408 void DrawDynamite(int x, int y)
5409 {
5410   int sx = SCREENX(x), sy = SCREENY(y);
5411   int graphic = el2img(Feld[x][y]);
5412   int frame;
5413
5414   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5415     return;
5416
5417   if (IS_WALKABLE_INSIDE(Back[x][y]))
5418     return;
5419
5420   if (Back[x][y])
5421     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422   else if (Store[x][y])
5423     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5424
5425   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5426
5427   if (Back[x][y] || Store[x][y])
5428     DrawGraphicThruMask(sx, sy, graphic, frame);
5429   else
5430     DrawGraphic(sx, sy, graphic, frame);
5431 }
5432
5433 void CheckDynamite(int x, int y)
5434 {
5435   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5436   {
5437     MovDelay[x][y]--;
5438
5439     if (MovDelay[x][y] != 0)
5440     {
5441       DrawDynamite(x, y);
5442       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5443
5444       return;
5445     }
5446   }
5447
5448   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5449
5450   Bang(x, y);
5451 }
5452
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5454 {
5455   boolean num_checked_players = 0;
5456   int i;
5457
5458   for (i = 0; i < MAX_PLAYERS; i++)
5459   {
5460     if (stored_player[i].active)
5461     {
5462       int sx = stored_player[i].jx;
5463       int sy = stored_player[i].jy;
5464
5465       if (num_checked_players == 0)
5466       {
5467         *sx1 = *sx2 = sx;
5468         *sy1 = *sy2 = sy;
5469       }
5470       else
5471       {
5472         *sx1 = MIN(*sx1, sx);
5473         *sy1 = MIN(*sy1, sy);
5474         *sx2 = MAX(*sx2, sx);
5475         *sy2 = MAX(*sy2, sy);
5476       }
5477
5478       num_checked_players++;
5479     }
5480   }
5481 }
5482
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5484 {
5485   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5486
5487   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5488
5489   return (sx2 - sx1 < SCR_FIELDX &&
5490           sy2 - sy1 < SCR_FIELDY);
5491 }
5492
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5494 {
5495   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5496
5497   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5498
5499   *sx = (sx1 + sx2) / 2;
5500   *sy = (sy1 + sy2) / 2;
5501 }
5502
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504                         boolean center_screen, boolean quick_relocation)
5505 {
5506   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507   boolean no_delay = (tape.warp_forward);
5508   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5510
5511   if (quick_relocation)
5512   {
5513     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5514     {
5515       if (!level.shifted_relocation || center_screen)
5516       {
5517         /* quick relocation (without scrolling), with centering of screen */
5518
5519         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5520                     x > SBX_Right + MIDPOSX ? SBX_Right :
5521                     x - MIDPOSX);
5522
5523         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5525                     y - MIDPOSY);
5526       }
5527       else
5528       {
5529         /* quick relocation (without scrolling), but do not center screen */
5530
5531         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5532                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5533                                old_x - MIDPOSX);
5534
5535         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5537                                old_y - MIDPOSY);
5538
5539         int offset_x = x + (scroll_x - center_scroll_x);
5540         int offset_y = y + (scroll_y - center_scroll_y);
5541
5542         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5543                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544                     offset_x - MIDPOSX);
5545
5546         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548                     offset_y - MIDPOSY);
5549       }
5550     }
5551     else
5552     {
5553 #if 1
5554       if (!level.shifted_relocation || center_screen)
5555       {
5556         /* quick relocation (without scrolling), with centering of screen */
5557
5558         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5559                     x > SBX_Right + MIDPOSX ? SBX_Right :
5560                     x - MIDPOSX);
5561
5562         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564                     y - MIDPOSY);
5565       }
5566       else
5567       {
5568         /* quick relocation (without scrolling), but do not center screen */
5569
5570         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5571                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5572                                old_x - MIDPOSX);
5573
5574         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5576                                old_y - MIDPOSY);
5577
5578         int offset_x = x + (scroll_x - center_scroll_x);
5579         int offset_y = y + (scroll_y - center_scroll_y);
5580
5581         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5582                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583                     offset_x - MIDPOSX);
5584
5585         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587                     offset_y - MIDPOSY);
5588       }
5589 #else
5590       /* quick relocation (without scrolling), inside visible screen area */
5591
5592       int offset = game.scroll_delay_value;
5593
5594       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5595           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5597
5598       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5599           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5601
5602       /* don't scroll over playfield boundaries */
5603       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5605
5606       /* don't scroll over playfield boundaries */
5607       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5609 #endif
5610     }
5611
5612     RedrawPlayfield(TRUE, 0,0,0,0);
5613   }
5614   else
5615   {
5616 #if 1
5617     int scroll_xx, scroll_yy;
5618
5619     if (!level.shifted_relocation || center_screen)
5620     {
5621       /* visible relocation (with scrolling), with centering of screen */
5622
5623       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5624                    x > SBX_Right + MIDPOSX ? SBX_Right :
5625                    x - MIDPOSX);
5626
5627       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5629                    y - MIDPOSY);
5630     }
5631     else
5632     {
5633       /* visible relocation (with scrolling), but do not center screen */
5634
5635       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5636                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5637                              old_x - MIDPOSX);
5638
5639       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5641                              old_y - MIDPOSY);
5642
5643       int offset_x = x + (scroll_x - center_scroll_x);
5644       int offset_y = y + (scroll_y - center_scroll_y);
5645
5646       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5647                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648                    offset_x - MIDPOSX);
5649
5650       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652                    offset_y - MIDPOSY);
5653     }
5654
5655 #else
5656
5657     /* visible relocation (with scrolling), with centering of screen */
5658
5659     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5660                      x > SBX_Right + MIDPOSX ? SBX_Right :
5661                      x - MIDPOSX);
5662
5663     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665                      y - MIDPOSY);
5666 #endif
5667
5668     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5669
5670     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5671     {
5672       int dx = 0, dy = 0;
5673       int fx = FX, fy = FY;
5674
5675       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5677
5678       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5679         break;
5680
5681       scroll_x -= dx;
5682       scroll_y -= dy;
5683
5684       fx += dx * TILEX / 2;
5685       fy += dy * TILEY / 2;
5686
5687       ScrollLevel(dx, dy);
5688       DrawAllPlayers();
5689
5690       /* scroll in two steps of half tile size to make things smoother */
5691       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5692       FlushDisplay();
5693       Delay(wait_delay_value);
5694
5695       /* scroll second step to align at full tile size */
5696       BackToFront();
5697       Delay(wait_delay_value);
5698     }
5699
5700     DrawAllPlayers();
5701     BackToFront();
5702     Delay(wait_delay_value);
5703   }
5704 }
5705
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5707 {
5708   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709   int player_nr = GET_PLAYER_NR(el_player);
5710   struct PlayerInfo *player = &stored_player[player_nr];
5711   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712   boolean no_delay = (tape.warp_forward);
5713   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715   int old_jx = player->jx;
5716   int old_jy = player->jy;
5717   int old_element = Feld[old_jx][old_jy];
5718   int element = Feld[jx][jy];
5719   boolean player_relocated = (old_jx != jx || old_jy != jy);
5720
5721   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5723   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5725   int leave_side_horiz = move_dir_horiz;
5726   int leave_side_vert  = move_dir_vert;
5727   int enter_side = enter_side_horiz | enter_side_vert;
5728   int leave_side = leave_side_horiz | leave_side_vert;
5729
5730   if (player->GameOver)         /* do not reanimate dead player */
5731     return;
5732
5733   if (!player_relocated)        /* no need to relocate the player */
5734     return;
5735
5736   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5737   {
5738     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5739     DrawLevelField(jx, jy);
5740   }
5741
5742   if (player->present)
5743   {
5744     while (player->MovPos)
5745     {
5746       ScrollPlayer(player, SCROLL_GO_ON);
5747       ScrollScreen(NULL, SCROLL_GO_ON);
5748
5749       AdvanceFrameAndPlayerCounters(player->index_nr);
5750
5751       DrawPlayer(player);
5752
5753       BackToFront();
5754       Delay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         /* needed here only to cleanup last field */
5758     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Feld[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5776   {
5777     Feld[jx][jy] = element;
5778     InitField(jx, jy, FALSE);
5779   }
5780
5781   /* only visually relocate centered player */
5782   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783                      FALSE, level.instant_relocation);
5784
5785   TestIfPlayerTouchesBadThing(jx, jy);
5786   TestIfPlayerTouchesCustomElement(jx, jy);
5787
5788   if (IS_CUSTOM_ELEMENT(element))
5789     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790                                player->index_bit, enter_side);
5791
5792   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793                                       player->index_bit, enter_side);
5794
5795 #if 1
5796   if (player->is_switching)
5797   {
5798     /* ensure that relocation while still switching an element does not cause
5799        a new element to be treated as also switched directly after relocation
5800        (this is important for teleporter switches that teleport the player to
5801        a place where another teleporter switch is in the same direction, which
5802        would then incorrectly be treated as immediately switched before the
5803        direction key that caused the switch was released) */
5804
5805     player->switch_x += jx - old_jx;
5806     player->switch_y += jy - old_jy;
5807   }
5808 #endif
5809 }
5810
5811 void Explode(int ex, int ey, int phase, int mode)
5812 {
5813   int x, y;
5814   int last_phase;
5815   int border_element;
5816
5817   /* !!! eliminate this variable !!! */
5818   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5819
5820   if (game.explosions_delayed)
5821   {
5822     ExplodeField[ex][ey] = mode;
5823     return;
5824   }
5825
5826   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5827   {
5828     int center_element = Feld[ex][ey];
5829     int artwork_element, explosion_element;     /* set these values later */
5830
5831 #if 0
5832     /* --- This is only really needed (and now handled) in "Impact()". --- */
5833     /* do not explode moving elements that left the explode field in time */
5834     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5835         center_element == EL_EMPTY &&
5836         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5837       return;
5838 #endif
5839
5840 #if 0
5841     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5842     if (mode == EX_TYPE_NORMAL ||
5843         mode == EX_TYPE_CENTER ||
5844         mode == EX_TYPE_CROSS)
5845       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5846 #endif
5847
5848     /* remove things displayed in background while burning dynamite */
5849     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5850       Back[ex][ey] = 0;
5851
5852     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5853     {
5854       /* put moving element to center field (and let it explode there) */
5855       center_element = MovingOrBlocked2Element(ex, ey);
5856       RemoveMovingField(ex, ey);
5857       Feld[ex][ey] = center_element;
5858     }
5859
5860     /* now "center_element" is finally determined -- set related values now */
5861     artwork_element = center_element;           /* for custom player artwork */
5862     explosion_element = center_element;         /* for custom player artwork */
5863
5864     if (IS_PLAYER(ex, ey))
5865     {
5866       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5867
5868       artwork_element = stored_player[player_nr].artwork_element;
5869
5870       if (level.use_explosion_element[player_nr])
5871       {
5872         explosion_element = level.explosion_element[player_nr];
5873         artwork_element = explosion_element;
5874       }
5875     }
5876
5877 #if 1
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882 #endif
5883
5884     last_phase = element_info[explosion_element].explosion_delay + 1;
5885
5886     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5887     {
5888       int xx = x - ex + 1;
5889       int yy = y - ey + 1;
5890       int element;
5891
5892       if (!IN_LEV_FIELD(x, y) ||
5893           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5895         continue;
5896
5897       element = Feld[x][y];
5898
5899       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5900       {
5901         element = MovingOrBlocked2Element(x, y);
5902
5903         if (!IS_EXPLOSION_PROOF(element))
5904           RemoveMovingField(x, y);
5905       }
5906
5907       /* indestructible elements can only explode in center (but not flames) */
5908       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909                                            mode == EX_TYPE_BORDER)) ||
5910           element == EL_FLAMES)
5911         continue;
5912
5913       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914          behaviour, for example when touching a yamyam that explodes to rocks
5915          with active deadly shield, a rock is created under the player !!! */
5916       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5917 #if 0
5918       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5921 #else
5922       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5923 #endif
5924       {
5925         if (IS_ACTIVE_BOMB(element))
5926         {
5927           /* re-activate things under the bomb like gate or penguin */
5928           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5929           Back[x][y] = 0;
5930         }
5931
5932         continue;
5933       }
5934
5935       /* save walkable background elements while explosion on same tile */
5936       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938         Back[x][y] = element;
5939
5940       /* ignite explodable elements reached by other explosion */
5941       if (element == EL_EXPLOSION)
5942         element = Store2[x][y];
5943
5944       if (AmoebaNr[x][y] &&
5945           (element == EL_AMOEBA_FULL ||
5946            element == EL_BD_AMOEBA ||
5947            element == EL_AMOEBA_GROWING))
5948       {
5949         AmoebaCnt[AmoebaNr[x][y]]--;
5950         AmoebaCnt2[AmoebaNr[x][y]]--;
5951       }
5952
5953       RemoveField(x, y);
5954
5955       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5956       {
5957         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5958
5959         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5960
5961         if (PLAYERINFO(ex, ey)->use_murphy)
5962           Store[x][y] = EL_EMPTY;
5963       }
5964
5965       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5966          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5967       else if (ELEM_IS_PLAYER(center_element))
5968         Store[x][y] = EL_EMPTY;
5969       else if (center_element == EL_YAMYAM)
5970         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972         Store[x][y] = element_info[center_element].content.e[xx][yy];
5973 #if 1
5974       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976          otherwise) -- FIX THIS !!! */
5977       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978         Store[x][y] = element_info[element].content.e[1][1];
5979 #else
5980       else if (!CAN_EXPLODE(element))
5981         Store[x][y] = element_info[element].content.e[1][1];
5982 #endif
5983       else
5984         Store[x][y] = EL_EMPTY;
5985
5986       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987           center_element == EL_AMOEBA_TO_DIAMOND)
5988         Store2[x][y] = element;
5989
5990       Feld[x][y] = EL_EXPLOSION;
5991       GfxElement[x][y] = artwork_element;
5992
5993       ExplodePhase[x][y] = 1;
5994       ExplodeDelay[x][y] = last_phase;
5995
5996       Stop[x][y] = TRUE;
5997     }
5998
5999     if (center_element == EL_YAMYAM)
6000       game.yamyam_content_nr =
6001         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6002
6003     return;
6004   }
6005
6006   if (Stop[ex][ey])
6007     return;
6008
6009   x = ex;
6010   y = ey;
6011
6012   if (phase == 1)
6013     GfxFrame[x][y] = 0;         /* restart explosion animation */
6014
6015   last_phase = ExplodeDelay[x][y];
6016
6017   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6018
6019 #ifdef DEBUG
6020
6021   /* activate this even in non-DEBUG version until cause for crash in
6022      getGraphicAnimationFrame() (see below) is found and eliminated */
6023
6024 #endif
6025 #if 1
6026
6027 #if 1
6028   /* this can happen if the player leaves an explosion just in time */
6029   if (GfxElement[x][y] == EL_UNDEFINED)
6030     GfxElement[x][y] = EL_EMPTY;
6031 #else
6032   if (GfxElement[x][y] == EL_UNDEFINED)
6033   {
6034     printf("\n\n");
6035     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6036     printf("Explode(): This should never happen!\n");
6037     printf("\n\n");
6038
6039     GfxElement[x][y] = EL_EMPTY;
6040   }
6041 #endif
6042
6043 #endif
6044
6045   border_element = Store2[x][y];
6046   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047     border_element = StorePlayer[x][y];
6048
6049   if (phase == element_info[border_element].ignition_delay ||
6050       phase == last_phase)
6051   {
6052     boolean border_explosion = FALSE;
6053
6054     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055         !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     {
6057       KillPlayerUnlessExplosionProtected(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6061     {
6062       Feld[x][y] = Store2[x][y];
6063       Store2[x][y] = 0;
6064       Bang(x, y);
6065       border_explosion = TRUE;
6066     }
6067     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6068     {
6069       AmoebeUmwandeln(x, y);
6070       Store2[x][y] = 0;
6071       border_explosion = TRUE;
6072     }
6073
6074     /* if an element just explodes due to another explosion (chain-reaction),
6075        do not immediately end the new explosion when it was the last frame of
6076        the explosion (as it would be done in the following "if"-statement!) */
6077     if (border_explosion && phase == last_phase)
6078       return;
6079   }
6080
6081   if (phase == last_phase)
6082   {
6083     int element;
6084
6085     element = Feld[x][y] = Store[x][y];
6086     Store[x][y] = Store2[x][y] = 0;
6087     GfxElement[x][y] = EL_UNDEFINED;
6088
6089     /* player can escape from explosions and might therefore be still alive */
6090     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091         element <= EL_PLAYER_IS_EXPLODING_4)
6092     {
6093       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094       int explosion_element = EL_PLAYER_1 + player_nr;
6095       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6097
6098       if (level.use_explosion_element[player_nr])
6099         explosion_element = level.explosion_element[player_nr];
6100
6101       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102                     element_info[explosion_element].content.e[xx][yy]);
6103     }
6104
6105     /* restore probably existing indestructible background element */
6106     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107       element = Feld[x][y] = Back[x][y];
6108     Back[x][y] = 0;
6109
6110     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111     GfxDir[x][y] = MV_NONE;
6112     ChangeDelay[x][y] = 0;
6113     ChangePage[x][y] = -1;
6114
6115 #if USE_NEW_CUSTOM_VALUE
6116     CustomValue[x][y] = 0;
6117 #endif
6118
6119     InitField_WithBug2(x, y, FALSE);
6120
6121     TEST_DrawLevelField(x, y);
6122
6123     TestIfElementTouchesCustomElement(x, y);
6124
6125     if (GFX_CRUMBLED(element))
6126       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6127
6128     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6129       StorePlayer[x][y] = 0;
6130
6131     if (ELEM_IS_PLAYER(element))
6132       RelocatePlayer(x, y, element);
6133   }
6134   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6135   {
6136     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6137     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6138
6139     if (phase == delay)
6140       TEST_DrawLevelFieldCrumbledSand(x, y);
6141
6142     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6143     {
6144       DrawLevelElement(x, y, Back[x][y]);
6145       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6146     }
6147     else if (IS_WALKABLE_UNDER(Back[x][y]))
6148     {
6149       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150       DrawLevelElementThruMask(x, y, Back[x][y]);
6151     }
6152     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6153       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6154   }
6155 }
6156
6157 void DynaExplode(int ex, int ey)
6158 {
6159   int i, j;
6160   int dynabomb_element = Feld[ex][ey];
6161   int dynabomb_size = 1;
6162   boolean dynabomb_xl = FALSE;
6163   struct PlayerInfo *player;
6164   static int xy[4][2] =
6165   {
6166     { 0, -1 },
6167     { -1, 0 },
6168     { +1, 0 },
6169     { 0, +1 }
6170   };
6171
6172   if (IS_ACTIVE_BOMB(dynabomb_element))
6173   {
6174     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6175     dynabomb_size = player->dynabomb_size;
6176     dynabomb_xl = player->dynabomb_xl;
6177     player->dynabombs_left++;
6178   }
6179
6180   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6181
6182   for (i = 0; i < NUM_DIRECTIONS; i++)
6183   {
6184     for (j = 1; j <= dynabomb_size; j++)
6185     {
6186       int x = ex + j * xy[i][0];
6187       int y = ey + j * xy[i][1];
6188       int element;
6189
6190       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6191         break;
6192
6193       element = Feld[x][y];
6194
6195       /* do not restart explosions of fields with active bombs */
6196       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6197         continue;
6198
6199       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6200
6201       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6202           !IS_DIGGABLE(element) && !dynabomb_xl)
6203         break;
6204     }
6205   }
6206 }
6207
6208 void Bang(int x, int y)
6209 {
6210   int element = MovingOrBlocked2Element(x, y);
6211   int explosion_type = EX_TYPE_NORMAL;
6212
6213   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6214   {
6215     struct PlayerInfo *player = PLAYERINFO(x, y);
6216
6217 #if USE_FIX_CE_ACTION_WITH_PLAYER
6218     element = Feld[x][y] = player->initial_element;
6219 #else
6220     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6221                             player->element_nr);
6222 #endif
6223
6224     if (level.use_explosion_element[player->index_nr])
6225     {
6226       int explosion_element = level.explosion_element[player->index_nr];
6227
6228       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6229         explosion_type = EX_TYPE_CROSS;
6230       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6231         explosion_type = EX_TYPE_CENTER;
6232     }
6233   }
6234
6235   switch (element)
6236   {
6237     case EL_BUG:
6238     case EL_SPACESHIP:
6239     case EL_BD_BUTTERFLY:
6240     case EL_BD_FIREFLY:
6241     case EL_YAMYAM:
6242     case EL_DARK_YAMYAM:
6243     case EL_ROBOT:
6244     case EL_PACMAN:
6245     case EL_MOLE:
6246       RaiseScoreElement(element);
6247       break;
6248
6249     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6250     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6251     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6252     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6253     case EL_DYNABOMB_INCREASE_NUMBER:
6254     case EL_DYNABOMB_INCREASE_SIZE:
6255     case EL_DYNABOMB_INCREASE_POWER:
6256       explosion_type = EX_TYPE_DYNA;
6257       break;
6258
6259     case EL_DC_LANDMINE:
6260 #if 0
6261     case EL_EM_EXIT_OPEN:
6262     case EL_EM_STEEL_EXIT_OPEN:
6263 #endif
6264       explosion_type = EX_TYPE_CENTER;
6265       break;
6266
6267     case EL_PENGUIN:
6268     case EL_LAMP:
6269     case EL_LAMP_ACTIVE:
6270     case EL_AMOEBA_TO_DIAMOND:
6271       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6272         explosion_type = EX_TYPE_CENTER;
6273       break;
6274
6275     default:
6276       if (element_info[element].explosion_type == EXPLODES_CROSS)
6277         explosion_type = EX_TYPE_CROSS;
6278       else if (element_info[element].explosion_type == EXPLODES_1X1)
6279         explosion_type = EX_TYPE_CENTER;
6280       break;
6281   }
6282
6283   if (explosion_type == EX_TYPE_DYNA)
6284     DynaExplode(x, y);
6285   else
6286     Explode(x, y, EX_PHASE_START, explosion_type);
6287
6288   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6289 }
6290
6291 void SplashAcid(int x, int y)
6292 {
6293   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6294       (!IN_LEV_FIELD(x - 1, y - 2) ||
6295        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6296     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6297
6298   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6299       (!IN_LEV_FIELD(x + 1, y - 2) ||
6300        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6301     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6302
6303   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6304 }
6305
6306 static void InitBeltMovement()
6307 {
6308   static int belt_base_element[4] =
6309   {
6310     EL_CONVEYOR_BELT_1_LEFT,
6311     EL_CONVEYOR_BELT_2_LEFT,
6312     EL_CONVEYOR_BELT_3_LEFT,
6313     EL_CONVEYOR_BELT_4_LEFT
6314   };
6315   static int belt_base_active_element[4] =
6316   {
6317     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6318     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6319     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6320     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6321   };
6322
6323   int x, y, i, j;
6324
6325   /* set frame order for belt animation graphic according to belt direction */
6326   for (i = 0; i < NUM_BELTS; i++)
6327   {
6328     int belt_nr = i;
6329
6330     for (j = 0; j < NUM_BELT_PARTS; j++)
6331     {
6332       int element = belt_base_active_element[belt_nr] + j;
6333       int graphic_1 = el2img(element);
6334       int graphic_2 = el2panelimg(element);
6335
6336       if (game.belt_dir[i] == MV_LEFT)
6337       {
6338         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6339         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6340       }
6341       else
6342       {
6343         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6344         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6345       }
6346     }
6347   }
6348
6349   SCAN_PLAYFIELD(x, y)
6350   {
6351     int element = Feld[x][y];
6352
6353     for (i = 0; i < NUM_BELTS; i++)
6354     {
6355       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6356       {
6357         int e_belt_nr = getBeltNrFromBeltElement(element);
6358         int belt_nr = i;
6359
6360         if (e_belt_nr == belt_nr)
6361         {
6362           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6363
6364           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6365         }
6366       }
6367     }
6368   }
6369 }
6370
6371 static void ToggleBeltSwitch(int x, int y)
6372 {
6373   static int belt_base_element[4] =
6374   {
6375     EL_CONVEYOR_BELT_1_LEFT,
6376     EL_CONVEYOR_BELT_2_LEFT,
6377     EL_CONVEYOR_BELT_3_LEFT,
6378     EL_CONVEYOR_BELT_4_LEFT
6379   };
6380   static int belt_base_active_element[4] =
6381   {
6382     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6383     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6384     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6385     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6386   };
6387   static int belt_base_switch_element[4] =
6388   {
6389     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6390     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6391     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6392     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6393   };
6394   static int belt_move_dir[4] =
6395   {
6396     MV_LEFT,
6397     MV_NONE,
6398     MV_RIGHT,
6399     MV_NONE,
6400   };
6401
6402   int element = Feld[x][y];
6403   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6404   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6405   int belt_dir = belt_move_dir[belt_dir_nr];
6406   int xx, yy, i;
6407
6408   if (!IS_BELT_SWITCH(element))
6409     return;
6410
6411   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6412   game.belt_dir[belt_nr] = belt_dir;
6413
6414   if (belt_dir_nr == 3)
6415     belt_dir_nr = 1;
6416
6417   /* set frame order for belt animation graphic according to belt direction */
6418   for (i = 0; i < NUM_BELT_PARTS; i++)
6419   {
6420     int element = belt_base_active_element[belt_nr] + i;
6421     int graphic_1 = el2img(element);
6422     int graphic_2 = el2panelimg(element);
6423
6424     if (belt_dir == MV_LEFT)
6425     {
6426       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6427       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6428     }
6429     else
6430     {
6431       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6432       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6433     }
6434   }
6435
6436   SCAN_PLAYFIELD(xx, yy)
6437   {
6438     int element = Feld[xx][yy];
6439
6440     if (IS_BELT_SWITCH(element))
6441     {
6442       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6443
6444       if (e_belt_nr == belt_nr)
6445       {
6446         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6447         TEST_DrawLevelField(xx, yy);
6448       }
6449     }
6450     else if (IS_BELT(element) && belt_dir != MV_NONE)
6451     {
6452       int e_belt_nr = getBeltNrFromBeltElement(element);
6453
6454       if (e_belt_nr == belt_nr)
6455       {
6456         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6457
6458         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6459         TEST_DrawLevelField(xx, yy);
6460       }
6461     }
6462     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6463     {
6464       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6465
6466       if (e_belt_nr == belt_nr)
6467       {
6468         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6469
6470         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6471         TEST_DrawLevelField(xx, yy);
6472       }
6473     }
6474   }
6475 }
6476
6477 static void ToggleSwitchgateSwitch(int x, int y)
6478 {
6479   int xx, yy;
6480
6481   game.switchgate_pos = !game.switchgate_pos;
6482
6483   SCAN_PLAYFIELD(xx, yy)
6484   {
6485     int element = Feld[xx][yy];
6486
6487 #if !USE_BOTH_SWITCHGATE_SWITCHES
6488     if (element == EL_SWITCHGATE_SWITCH_UP ||
6489         element == EL_SWITCHGATE_SWITCH_DOWN)
6490     {
6491       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6495              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6496     {
6497       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6498       TEST_DrawLevelField(xx, yy);
6499     }
6500 #else
6501     if (element == EL_SWITCHGATE_SWITCH_UP)
6502     {
6503       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6504       TEST_DrawLevelField(xx, yy);
6505     }
6506     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6507     {
6508       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6509       TEST_DrawLevelField(xx, yy);
6510     }
6511     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6512     {
6513       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6514       TEST_DrawLevelField(xx, yy);
6515     }
6516     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6517     {
6518       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6519       TEST_DrawLevelField(xx, yy);
6520     }
6521 #endif
6522     else if (element == EL_SWITCHGATE_OPEN ||
6523              element == EL_SWITCHGATE_OPENING)
6524     {
6525       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6526
6527       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6528     }
6529     else if (element == EL_SWITCHGATE_CLOSED ||
6530              element == EL_SWITCHGATE_CLOSING)
6531     {
6532       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6533
6534       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6535     }
6536   }
6537 }
6538
6539 static int getInvisibleActiveFromInvisibleElement(int element)
6540 {
6541   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6542           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6543           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6544           element);
6545 }
6546
6547 static int getInvisibleFromInvisibleActiveElement(int element)
6548 {
6549   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6550           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6551           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6552           element);
6553 }
6554
6555 static void RedrawAllLightSwitchesAndInvisibleElements()
6556 {
6557   int x, y;
6558
6559   SCAN_PLAYFIELD(x, y)
6560   {
6561     int element = Feld[x][y];
6562
6563     if (element == EL_LIGHT_SWITCH &&
6564         game.light_time_left > 0)
6565     {
6566       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6567       TEST_DrawLevelField(x, y);
6568     }
6569     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6570              game.light_time_left == 0)
6571     {
6572       Feld[x][y] = EL_LIGHT_SWITCH;
6573       TEST_DrawLevelField(x, y);
6574     }
6575     else if (element == EL_EMC_DRIPPER &&
6576              game.light_time_left > 0)
6577     {
6578       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582              game.light_time_left == 0)
6583     {
6584       Feld[x][y] = EL_EMC_DRIPPER;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL ||
6588              element == EL_INVISIBLE_WALL ||
6589              element == EL_INVISIBLE_SAND)
6590     {
6591       if (game.light_time_left > 0)
6592         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       /* uncrumble neighbour fields, if needed */
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6599     }
6600     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601              element == EL_INVISIBLE_WALL_ACTIVE ||
6602              element == EL_INVISIBLE_SAND_ACTIVE)
6603     {
6604       if (game.light_time_left == 0)
6605         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6606
6607       TEST_DrawLevelField(x, y);
6608
6609       /* re-crumble neighbour fields, if needed */
6610       if (element == EL_INVISIBLE_SAND)
6611         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6612     }
6613   }
6614 }
6615
6616 static void RedrawAllInvisibleElementsForLenses()
6617 {
6618   int x, y;
6619
6620   SCAN_PLAYFIELD(x, y)
6621   {
6622     int element = Feld[x][y];
6623
6624     if (element == EL_EMC_DRIPPER &&
6625         game.lenses_time_left > 0)
6626     {
6627       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631              game.lenses_time_left == 0)
6632     {
6633       Feld[x][y] = EL_EMC_DRIPPER;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_INVISIBLE_STEELWALL ||
6637              element == EL_INVISIBLE_WALL ||
6638              element == EL_INVISIBLE_SAND)
6639     {
6640       if (game.lenses_time_left > 0)
6641         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6642
6643       TEST_DrawLevelField(x, y);
6644
6645       /* uncrumble neighbour fields, if needed */
6646       if (element == EL_INVISIBLE_SAND)
6647         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6648     }
6649     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650              element == EL_INVISIBLE_WALL_ACTIVE ||
6651              element == EL_INVISIBLE_SAND_ACTIVE)
6652     {
6653       if (game.lenses_time_left == 0)
6654         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6655
6656       TEST_DrawLevelField(x, y);
6657
6658       /* re-crumble neighbour fields, if needed */
6659       if (element == EL_INVISIBLE_SAND)
6660         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6661     }
6662   }
6663 }
6664
6665 static void RedrawAllInvisibleElementsForMagnifier()
6666 {
6667   int x, y;
6668
6669   SCAN_PLAYFIELD(x, y)
6670   {
6671     int element = Feld[x][y];
6672
6673     if (element == EL_EMC_FAKE_GRASS &&
6674         game.magnify_time_left > 0)
6675     {
6676       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6677       TEST_DrawLevelField(x, y);
6678     }
6679     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6680              game.magnify_time_left == 0)
6681     {
6682       Feld[x][y] = EL_EMC_FAKE_GRASS;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (IS_GATE_GRAY(element) &&
6686              game.magnify_time_left > 0)
6687     {
6688       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6689                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6690                     IS_EM_GATE_GRAY(element) ?
6691                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6692                     IS_EMC_GATE_GRAY(element) ?
6693                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6694                     IS_DC_GATE_GRAY(element) ?
6695                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6696                     element);
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (IS_GATE_GRAY_ACTIVE(element) &&
6700              game.magnify_time_left == 0)
6701     {
6702       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6703                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6704                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6705                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6706                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6707                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6708                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6709                     EL_DC_GATE_WHITE_GRAY :
6710                     element);
6711       TEST_DrawLevelField(x, y);
6712     }
6713   }
6714 }
6715
6716 static void ToggleLightSwitch(int x, int y)
6717 {
6718   int element = Feld[x][y];
6719
6720   game.light_time_left =
6721     (element == EL_LIGHT_SWITCH ?
6722      level.time_light * FRAMES_PER_SECOND : 0);
6723
6724   RedrawAllLightSwitchesAndInvisibleElements();
6725 }
6726
6727 static void ActivateTimegateSwitch(int x, int y)
6728 {
6729   int xx, yy;
6730
6731   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6732
6733   SCAN_PLAYFIELD(xx, yy)
6734   {
6735     int element = Feld[xx][yy];
6736
6737     if (element == EL_TIMEGATE_CLOSED ||
6738         element == EL_TIMEGATE_CLOSING)
6739     {
6740       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6741       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6742     }
6743
6744     /*
6745     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6746     {
6747       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6748       TEST_DrawLevelField(xx, yy);
6749     }
6750     */
6751
6752   }
6753
6754 #if 1
6755   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6756                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6757 #else
6758   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6759 #endif
6760 }
6761
6762 void Impact(int x, int y)
6763 {
6764   boolean last_line = (y == lev_fieldy - 1);
6765   boolean object_hit = FALSE;
6766   boolean impact = (last_line || object_hit);
6767   int element = Feld[x][y];
6768   int smashed = EL_STEELWALL;
6769
6770   if (!last_line)       /* check if element below was hit */
6771   {
6772     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6773       return;
6774
6775     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6776                                          MovDir[x][y + 1] != MV_DOWN ||
6777                                          MovPos[x][y + 1] <= TILEY / 2));
6778
6779     /* do not smash moving elements that left the smashed field in time */
6780     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6781         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6782       object_hit = FALSE;
6783
6784 #if USE_QUICKSAND_IMPACT_BUGFIX
6785     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6786     {
6787       RemoveMovingField(x, y + 1);
6788       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6789       Feld[x][y + 2] = EL_ROCK;
6790       TEST_DrawLevelField(x, y + 2);
6791
6792       object_hit = TRUE;
6793     }
6794
6795     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6796     {
6797       RemoveMovingField(x, y + 1);
6798       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6799       Feld[x][y + 2] = EL_ROCK;
6800       TEST_DrawLevelField(x, y + 2);
6801
6802       object_hit = TRUE;
6803     }
6804 #endif
6805
6806     if (object_hit)
6807       smashed = MovingOrBlocked2Element(x, y + 1);
6808
6809     impact = (last_line || object_hit);
6810   }
6811
6812   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6813   {
6814     SplashAcid(x, y + 1);
6815     return;
6816   }
6817
6818   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6819   /* only reset graphic animation if graphic really changes after impact */
6820   if (impact &&
6821       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6822   {
6823     ResetGfxAnimation(x, y);
6824     TEST_DrawLevelField(x, y);
6825   }
6826
6827   if (impact && CAN_EXPLODE_IMPACT(element))
6828   {
6829     Bang(x, y);
6830     return;
6831   }
6832   else if (impact && element == EL_PEARL &&
6833            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6834   {
6835     ResetGfxAnimation(x, y);
6836
6837     Feld[x][y] = EL_PEARL_BREAKING;
6838     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6839     return;
6840   }
6841   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6842   {
6843     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6844
6845     return;
6846   }
6847
6848   if (impact && element == EL_AMOEBA_DROP)
6849   {
6850     if (object_hit && IS_PLAYER(x, y + 1))
6851       KillPlayerUnlessEnemyProtected(x, y + 1);
6852     else if (object_hit && smashed == EL_PENGUIN)
6853       Bang(x, y + 1);
6854     else
6855     {
6856       Feld[x][y] = EL_AMOEBA_GROWING;
6857       Store[x][y] = EL_AMOEBA_WET;
6858
6859       ResetRandomAnimationValue(x, y);
6860     }
6861     return;
6862   }
6863
6864   if (object_hit)               /* check which object was hit */
6865   {
6866     if ((CAN_PASS_MAGIC_WALL(element) && 
6867          (smashed == EL_MAGIC_WALL ||
6868           smashed == EL_BD_MAGIC_WALL)) ||
6869         (CAN_PASS_DC_MAGIC_WALL(element) &&
6870          smashed == EL_DC_MAGIC_WALL))
6871     {
6872       int xx, yy;
6873       int activated_magic_wall =
6874         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6875          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6876          EL_DC_MAGIC_WALL_ACTIVE);
6877
6878       /* activate magic wall / mill */
6879       SCAN_PLAYFIELD(xx, yy)
6880       {
6881         if (Feld[xx][yy] == smashed)
6882           Feld[xx][yy] = activated_magic_wall;
6883       }
6884
6885       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6886       game.magic_wall_active = TRUE;
6887
6888       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6889                             SND_MAGIC_WALL_ACTIVATING :
6890                             smashed == EL_BD_MAGIC_WALL ?
6891                             SND_BD_MAGIC_WALL_ACTIVATING :
6892                             SND_DC_MAGIC_WALL_ACTIVATING));
6893     }
6894
6895     if (IS_PLAYER(x, y + 1))
6896     {
6897       if (CAN_SMASH_PLAYER(element))
6898       {
6899         KillPlayerUnlessEnemyProtected(x, y + 1);
6900         return;
6901       }
6902     }
6903     else if (smashed == EL_PENGUIN)
6904     {
6905       if (CAN_SMASH_PLAYER(element))
6906       {
6907         Bang(x, y + 1);
6908         return;
6909       }
6910     }
6911     else if (element == EL_BD_DIAMOND)
6912     {
6913       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6914       {
6915         Bang(x, y + 1);
6916         return;
6917       }
6918     }
6919     else if (((element == EL_SP_INFOTRON ||
6920                element == EL_SP_ZONK) &&
6921               (smashed == EL_SP_SNIKSNAK ||
6922                smashed == EL_SP_ELECTRON ||
6923                smashed == EL_SP_DISK_ORANGE)) ||
6924              (element == EL_SP_INFOTRON &&
6925               smashed == EL_SP_DISK_YELLOW))
6926     {
6927       Bang(x, y + 1);
6928       return;
6929     }
6930     else if (CAN_SMASH_EVERYTHING(element))
6931     {
6932       if (IS_CLASSIC_ENEMY(smashed) ||
6933           CAN_EXPLODE_SMASHED(smashed))
6934       {
6935         Bang(x, y + 1);
6936         return;
6937       }
6938       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6939       {
6940         if (smashed == EL_LAMP ||
6941             smashed == EL_LAMP_ACTIVE)
6942         {
6943           Bang(x, y + 1);
6944           return;
6945         }
6946         else if (smashed == EL_NUT)
6947         {
6948           Feld[x][y + 1] = EL_NUT_BREAKING;
6949           PlayLevelSound(x, y, SND_NUT_BREAKING);
6950           RaiseScoreElement(EL_NUT);
6951           return;
6952         }
6953         else if (smashed == EL_PEARL)
6954         {
6955           ResetGfxAnimation(x, y);
6956
6957           Feld[x][y + 1] = EL_PEARL_BREAKING;
6958           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6959           return;
6960         }
6961         else if (smashed == EL_DIAMOND)
6962         {
6963           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6964           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6965           return;
6966         }
6967         else if (IS_BELT_SWITCH(smashed))
6968         {
6969           ToggleBeltSwitch(x, y + 1);
6970         }
6971         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6972                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6973                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6974                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6975         {
6976           ToggleSwitchgateSwitch(x, y + 1);
6977         }
6978         else if (smashed == EL_LIGHT_SWITCH ||
6979                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6980         {
6981           ToggleLightSwitch(x, y + 1);
6982         }
6983         else
6984         {
6985 #if 0
6986           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6987 #endif
6988
6989           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6990
6991           CheckElementChangeBySide(x, y + 1, smashed, element,
6992                                    CE_SWITCHED, CH_SIDE_TOP);
6993           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6994                                             CH_SIDE_TOP);
6995         }
6996       }
6997       else
6998       {
6999         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7000       }
7001     }
7002   }
7003
7004   /* play sound of magic wall / mill */
7005   if (!last_line &&
7006       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7007        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7008        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7009   {
7010     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7011       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7012     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7013       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7014     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7015       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7016
7017     return;
7018   }
7019
7020   /* play sound of object that hits the ground */
7021   if (last_line || object_hit)
7022     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7023 }
7024
7025 inline static void TurnRoundExt(int x, int y)
7026 {
7027   static struct
7028   {
7029     int dx, dy;
7030   } move_xy[] =
7031   {
7032     {  0,  0 },
7033     { -1,  0 },
7034     { +1,  0 },
7035     {  0,  0 },
7036     {  0, -1 },
7037     {  0,  0 }, { 0, 0 }, { 0, 0 },
7038     {  0, +1 }
7039   };
7040   static struct
7041   {
7042     int left, right, back;
7043   } turn[] =
7044   {
7045     { 0,        0,              0        },
7046     { MV_DOWN,  MV_UP,          MV_RIGHT },
7047     { MV_UP,    MV_DOWN,        MV_LEFT  },
7048     { 0,        0,              0        },
7049     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7050     { 0,        0,              0        },
7051     { 0,        0,              0        },
7052     { 0,        0,              0        },
7053     { MV_RIGHT, MV_LEFT,        MV_UP    }
7054   };
7055
7056   int element = Feld[x][y];
7057   int move_pattern = element_info[element].move_pattern;
7058
7059   int old_move_dir = MovDir[x][y];
7060   int left_dir  = turn[old_move_dir].left;
7061   int right_dir = turn[old_move_dir].right;
7062   int back_dir  = turn[old_move_dir].back;
7063
7064   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7065   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7066   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7067   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7068
7069   int left_x  = x + left_dx,  left_y  = y + left_dy;
7070   int right_x = x + right_dx, right_y = y + right_dy;
7071   int move_x  = x + move_dx,  move_y  = y + move_dy;
7072
7073   int xx, yy;
7074
7075   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7076   {
7077     TestIfBadThingTouchesOtherBadThing(x, y);
7078
7079     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7080       MovDir[x][y] = right_dir;
7081     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7082       MovDir[x][y] = left_dir;
7083
7084     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7085       MovDelay[x][y] = 9;
7086     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7087       MovDelay[x][y] = 1;
7088   }
7089   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7090   {
7091     TestIfBadThingTouchesOtherBadThing(x, y);
7092
7093     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7094       MovDir[x][y] = left_dir;
7095     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7096       MovDir[x][y] = right_dir;
7097
7098     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7099       MovDelay[x][y] = 9;
7100     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7101       MovDelay[x][y] = 1;
7102   }
7103   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7104   {
7105     TestIfBadThingTouchesOtherBadThing(x, y);
7106
7107     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7108       MovDir[x][y] = left_dir;
7109     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7110       MovDir[x][y] = right_dir;
7111
7112     if (MovDir[x][y] != old_move_dir)
7113       MovDelay[x][y] = 9;
7114   }
7115   else if (element == EL_YAMYAM)
7116   {
7117     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7118     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7119
7120     if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7122     else if (can_turn_left)
7123       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7124     else if (can_turn_right)
7125       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     MovDelay[x][y] = 16 + 16 * RND(3);
7130   }
7131   else if (element == EL_DARK_YAMYAM)
7132   {
7133     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7134                                                          left_x, left_y);
7135     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7136                                                          right_x, right_y);
7137
7138     if (can_turn_left && can_turn_right)
7139       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7140     else if (can_turn_left)
7141       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7142     else if (can_turn_right)
7143       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7144     else
7145       MovDir[x][y] = back_dir;
7146
7147     MovDelay[x][y] = 16 + 16 * RND(3);
7148   }
7149   else if (element == EL_PACMAN)
7150   {
7151     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7152     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7153
7154     if (can_turn_left && can_turn_right)
7155       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7156     else if (can_turn_left)
7157       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7158     else if (can_turn_right)
7159       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7160     else
7161       MovDir[x][y] = back_dir;
7162
7163     MovDelay[x][y] = 6 + RND(40);
7164   }
7165   else if (element == EL_PIG)
7166   {
7167     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7168     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7169     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7170     boolean should_turn_left, should_turn_right, should_move_on;
7171     int rnd_value = 24;
7172     int rnd = RND(rnd_value);
7173
7174     should_turn_left = (can_turn_left &&
7175                         (!can_move_on ||
7176                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7177                                                    y + back_dy + left_dy)));
7178     should_turn_right = (can_turn_right &&
7179                          (!can_move_on ||
7180                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7181                                                     y + back_dy + right_dy)));
7182     should_move_on = (can_move_on &&
7183                       (!can_turn_left ||
7184                        !can_turn_right ||
7185                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7186                                                  y + move_dy + left_dy) ||
7187                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7188                                                  y + move_dy + right_dy)));
7189
7190     if (should_turn_left || should_turn_right || should_move_on)
7191     {
7192       if (should_turn_left && should_turn_right && should_move_on)
7193         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7194                         rnd < 2 * rnd_value / 3 ? right_dir :
7195                         old_move_dir);
7196       else if (should_turn_left && should_turn_right)
7197         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7198       else if (should_turn_left && should_move_on)
7199         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7200       else if (should_turn_right && should_move_on)
7201         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7202       else if (should_turn_left)
7203         MovDir[x][y] = left_dir;
7204       else if (should_turn_right)
7205         MovDir[x][y] = right_dir;
7206       else if (should_move_on)
7207         MovDir[x][y] = old_move_dir;
7208     }
7209     else if (can_move_on && rnd > rnd_value / 8)
7210       MovDir[x][y] = old_move_dir;
7211     else if (can_turn_left && can_turn_right)
7212       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213     else if (can_turn_left && rnd > rnd_value / 8)
7214       MovDir[x][y] = left_dir;
7215     else if (can_turn_right && rnd > rnd_value/8)
7216       MovDir[x][y] = right_dir;
7217     else
7218       MovDir[x][y] = back_dir;
7219
7220     xx = x + move_xy[MovDir[x][y]].dx;
7221     yy = y + move_xy[MovDir[x][y]].dy;
7222
7223     if (!IN_LEV_FIELD(xx, yy) ||
7224         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7225       MovDir[x][y] = old_move_dir;
7226
7227     MovDelay[x][y] = 0;
7228   }
7229   else if (element == EL_DRAGON)
7230   {
7231     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7232     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7233     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7234     int rnd_value = 24;
7235     int rnd = RND(rnd_value);
7236
7237     if (can_move_on && rnd > rnd_value / 8)
7238       MovDir[x][y] = old_move_dir;
7239     else if (can_turn_left && can_turn_right)
7240       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7241     else if (can_turn_left && rnd > rnd_value / 8)
7242       MovDir[x][y] = left_dir;
7243     else if (can_turn_right && rnd > rnd_value / 8)
7244       MovDir[x][y] = right_dir;
7245     else
7246       MovDir[x][y] = back_dir;
7247
7248     xx = x + move_xy[MovDir[x][y]].dx;
7249     yy = y + move_xy[MovDir[x][y]].dy;
7250
7251     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7252       MovDir[x][y] = old_move_dir;
7253
7254     MovDelay[x][y] = 0;
7255   }
7256   else if (element == EL_MOLE)
7257   {
7258     boolean can_move_on =
7259       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7260                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7261                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7262     if (!can_move_on)
7263     {
7264       boolean can_turn_left =
7265         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7266                               IS_AMOEBOID(Feld[left_x][left_y])));
7267
7268       boolean can_turn_right =
7269         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7270                               IS_AMOEBOID(Feld[right_x][right_y])));
7271
7272       if (can_turn_left && can_turn_right)
7273         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7274       else if (can_turn_left)
7275         MovDir[x][y] = left_dir;
7276       else
7277         MovDir[x][y] = right_dir;
7278     }
7279
7280     if (MovDir[x][y] != old_move_dir)
7281       MovDelay[x][y] = 9;
7282   }
7283   else if (element == EL_BALLOON)
7284   {
7285     MovDir[x][y] = game.wind_direction;
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (element == EL_SPRING)
7289   {
7290 #if USE_NEW_SPRING_BUMPER
7291     if (MovDir[x][y] & MV_HORIZONTAL)
7292     {
7293       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7294           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7295       {
7296         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7297         ResetGfxAnimation(move_x, move_y);
7298         TEST_DrawLevelField(move_x, move_y);
7299
7300         MovDir[x][y] = back_dir;
7301       }
7302       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7303                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304         MovDir[x][y] = MV_NONE;
7305     }
7306 #else
7307     if (MovDir[x][y] & MV_HORIZONTAL &&
7308         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7309          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7310       MovDir[x][y] = MV_NONE;
7311 #endif
7312
7313     MovDelay[x][y] = 0;
7314   }
7315   else if (element == EL_ROBOT ||
7316            element == EL_SATELLITE ||
7317            element == EL_PENGUIN ||
7318            element == EL_EMC_ANDROID)
7319   {
7320     int attr_x = -1, attr_y = -1;
7321
7322     if (AllPlayersGone)
7323     {
7324       attr_x = ExitX;
7325       attr_y = ExitY;
7326     }
7327     else
7328     {
7329       int i;
7330
7331       for (i = 0; i < MAX_PLAYERS; i++)
7332       {
7333         struct PlayerInfo *player = &stored_player[i];
7334         int jx = player->jx, jy = player->jy;
7335
7336         if (!player->active)
7337           continue;
7338
7339         if (attr_x == -1 ||
7340             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7341         {
7342           attr_x = jx;
7343           attr_y = jy;
7344         }
7345       }
7346     }
7347
7348     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7349         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7350          game.engine_version < VERSION_IDENT(3,1,0,0)))
7351     {
7352       attr_x = ZX;
7353       attr_y = ZY;
7354     }
7355
7356     if (element == EL_PENGUIN)
7357     {
7358       int i;
7359       static int xy[4][2] =
7360       {
7361         { 0, -1 },
7362         { -1, 0 },
7363         { +1, 0 },
7364         { 0, +1 }
7365       };
7366
7367       for (i = 0; i < NUM_DIRECTIONS; i++)
7368       {
7369         int ex = x + xy[i][0];
7370         int ey = y + xy[i][1];
7371
7372         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7373                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7374                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7375                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7376         {
7377           attr_x = ex;
7378           attr_y = ey;
7379           break;
7380         }
7381       }
7382     }
7383
7384     MovDir[x][y] = MV_NONE;
7385     if (attr_x < x)
7386       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7387     else if (attr_x > x)
7388       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7389     if (attr_y < y)
7390       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7391     else if (attr_y > y)
7392       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7393
7394     if (element == EL_ROBOT)
7395     {
7396       int newx, newy;
7397
7398       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7399         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7400       Moving2Blocked(x, y, &newx, &newy);
7401
7402       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7403         MovDelay[x][y] = 8 + 8 * !RND(3);
7404       else
7405         MovDelay[x][y] = 16;
7406     }
7407     else if (element == EL_PENGUIN)
7408     {
7409       int newx, newy;
7410
7411       MovDelay[x][y] = 1;
7412
7413       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7414       {
7415         boolean first_horiz = RND(2);
7416         int new_move_dir = MovDir[x][y];
7417
7418         MovDir[x][y] =
7419           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420         Moving2Blocked(x, y, &newx, &newy);
7421
7422         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7423           return;
7424
7425         MovDir[x][y] =
7426           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7427         Moving2Blocked(x, y, &newx, &newy);
7428
7429         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7430           return;
7431
7432         MovDir[x][y] = old_move_dir;
7433         return;
7434       }
7435     }
7436     else if (element == EL_SATELLITE)
7437     {
7438       int newx, newy;
7439
7440       MovDelay[x][y] = 1;
7441
7442       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443       {
7444         boolean first_horiz = RND(2);
7445         int new_move_dir = MovDir[x][y];
7446
7447         MovDir[x][y] =
7448           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449         Moving2Blocked(x, y, &newx, &newy);
7450
7451         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7452           return;
7453
7454         MovDir[x][y] =
7455           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456         Moving2Blocked(x, y, &newx, &newy);
7457
7458         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7459           return;
7460
7461         MovDir[x][y] = old_move_dir;
7462         return;
7463       }
7464     }
7465     else if (element == EL_EMC_ANDROID)
7466     {
7467       static int check_pos[16] =
7468       {
7469         -1,             /*  0 => (invalid)          */
7470         7,              /*  1 => MV_LEFT            */
7471         3,              /*  2 => MV_RIGHT           */
7472         -1,             /*  3 => (invalid)          */
7473         1,              /*  4 =>            MV_UP   */
7474         0,              /*  5 => MV_LEFT  | MV_UP   */
7475         2,              /*  6 => MV_RIGHT | MV_UP   */
7476         -1,             /*  7 => (invalid)          */
7477         5,              /*  8 =>            MV_DOWN */
7478         6,              /*  9 => MV_LEFT  | MV_DOWN */
7479         4,              /* 10 => MV_RIGHT | MV_DOWN */
7480         -1,             /* 11 => (invalid)          */
7481         -1,             /* 12 => (invalid)          */
7482         -1,             /* 13 => (invalid)          */
7483         -1,             /* 14 => (invalid)          */
7484         -1,             /* 15 => (invalid)          */
7485       };
7486       static struct
7487       {
7488         int dx, dy;
7489         int dir;
7490       } check_xy[8] =
7491       {
7492         { -1, -1,       MV_LEFT  | MV_UP   },
7493         {  0, -1,                  MV_UP   },
7494         { +1, -1,       MV_RIGHT | MV_UP   },
7495         { +1,  0,       MV_RIGHT           },
7496         { +1, +1,       MV_RIGHT | MV_DOWN },
7497         {  0, +1,                  MV_DOWN },
7498         { -1, +1,       MV_LEFT  | MV_DOWN },
7499         { -1,  0,       MV_LEFT            },
7500       };
7501       int start_pos, check_order;
7502       boolean can_clone = FALSE;
7503       int i;
7504
7505       /* check if there is any free field around current position */
7506       for (i = 0; i < 8; i++)
7507       {
7508         int newx = x + check_xy[i].dx;
7509         int newy = y + check_xy[i].dy;
7510
7511         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512         {
7513           can_clone = TRUE;
7514
7515           break;
7516         }
7517       }
7518
7519       if (can_clone)            /* randomly find an element to clone */
7520       {
7521         can_clone = FALSE;
7522
7523         start_pos = check_pos[RND(8)];
7524         check_order = (RND(2) ? -1 : +1);
7525
7526         for (i = 0; i < 8; i++)
7527         {
7528           int pos_raw = start_pos + i * check_order;
7529           int pos = (pos_raw + 8) % 8;
7530           int newx = x + check_xy[pos].dx;
7531           int newy = y + check_xy[pos].dy;
7532
7533           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7534           {
7535             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7536             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7537
7538             Store[x][y] = Feld[newx][newy];
7539
7540             can_clone = TRUE;
7541
7542             break;
7543           }
7544         }
7545       }
7546
7547       if (can_clone)            /* randomly find a direction to move */
7548       {
7549         can_clone = FALSE;
7550
7551         start_pos = check_pos[RND(8)];
7552         check_order = (RND(2) ? -1 : +1);
7553
7554         for (i = 0; i < 8; i++)
7555         {
7556           int pos_raw = start_pos + i * check_order;
7557           int pos = (pos_raw + 8) % 8;
7558           int newx = x + check_xy[pos].dx;
7559           int newy = y + check_xy[pos].dy;
7560           int new_move_dir = check_xy[pos].dir;
7561
7562           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7563           {
7564             MovDir[x][y] = new_move_dir;
7565             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7566
7567             can_clone = TRUE;
7568
7569             break;
7570           }
7571         }
7572       }
7573
7574       if (can_clone)            /* cloning and moving successful */
7575         return;
7576
7577       /* cannot clone -- try to move towards player */
7578
7579       start_pos = check_pos[MovDir[x][y] & 0x0f];
7580       check_order = (RND(2) ? -1 : +1);
7581
7582       for (i = 0; i < 3; i++)
7583       {
7584         /* first check start_pos, then previous/next or (next/previous) pos */
7585         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7586         int pos = (pos_raw + 8) % 8;
7587         int newx = x + check_xy[pos].dx;
7588         int newy = y + check_xy[pos].dy;
7589         int new_move_dir = check_xy[pos].dir;
7590
7591         if (IS_PLAYER(newx, newy))
7592           break;
7593
7594         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7595         {
7596           MovDir[x][y] = new_move_dir;
7597           MovDelay[x][y] = level.android_move_time * 8 + 1;
7598
7599           break;
7600         }
7601       }
7602     }
7603   }
7604   else if (move_pattern == MV_TURNING_LEFT ||
7605            move_pattern == MV_TURNING_RIGHT ||
7606            move_pattern == MV_TURNING_LEFT_RIGHT ||
7607            move_pattern == MV_TURNING_RIGHT_LEFT ||
7608            move_pattern == MV_TURNING_RANDOM ||
7609            move_pattern == MV_ALL_DIRECTIONS)
7610   {
7611     boolean can_turn_left =
7612       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7613     boolean can_turn_right =
7614       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7615
7616     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7617       return;
7618
7619     if (move_pattern == MV_TURNING_LEFT)
7620       MovDir[x][y] = left_dir;
7621     else if (move_pattern == MV_TURNING_RIGHT)
7622       MovDir[x][y] = right_dir;
7623     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7624       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7625     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7626       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7627     else if (move_pattern == MV_TURNING_RANDOM)
7628       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7629                       can_turn_right && !can_turn_left ? right_dir :
7630                       RND(2) ? left_dir : right_dir);
7631     else if (can_turn_left && can_turn_right)
7632       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7633     else if (can_turn_left)
7634       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7635     else if (can_turn_right)
7636       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7637     else
7638       MovDir[x][y] = back_dir;
7639
7640     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641   }
7642   else if (move_pattern == MV_HORIZONTAL ||
7643            move_pattern == MV_VERTICAL)
7644   {
7645     if (move_pattern & old_move_dir)
7646       MovDir[x][y] = back_dir;
7647     else if (move_pattern == MV_HORIZONTAL)
7648       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7649     else if (move_pattern == MV_VERTICAL)
7650       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7651
7652     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7653   }
7654   else if (move_pattern & MV_ANY_DIRECTION)
7655   {
7656     MovDir[x][y] = move_pattern;
7657     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7658   }
7659   else if (move_pattern & MV_WIND_DIRECTION)
7660   {
7661     MovDir[x][y] = game.wind_direction;
7662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7663   }
7664   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7665   {
7666     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7667       MovDir[x][y] = left_dir;
7668     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7669       MovDir[x][y] = right_dir;
7670
7671     if (MovDir[x][y] != old_move_dir)
7672       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7673   }
7674   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7675   {
7676     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7677       MovDir[x][y] = right_dir;
7678     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7679       MovDir[x][y] = left_dir;
7680
7681     if (MovDir[x][y] != old_move_dir)
7682       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683   }
7684   else if (move_pattern == MV_TOWARDS_PLAYER ||
7685            move_pattern == MV_AWAY_FROM_PLAYER)
7686   {
7687     int attr_x = -1, attr_y = -1;
7688     int newx, newy;
7689     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7690
7691     if (AllPlayersGone)
7692     {
7693       attr_x = ExitX;
7694       attr_y = ExitY;
7695     }
7696     else
7697     {
7698       int i;
7699
7700       for (i = 0; i < MAX_PLAYERS; i++)
7701       {
7702         struct PlayerInfo *player = &stored_player[i];
7703         int jx = player->jx, jy = player->jy;
7704
7705         if (!player->active)
7706           continue;
7707
7708         if (attr_x == -1 ||
7709             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7710         {
7711           attr_x = jx;
7712           attr_y = jy;
7713         }
7714       }
7715     }
7716
7717     MovDir[x][y] = MV_NONE;
7718     if (attr_x < x)
7719       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7720     else if (attr_x > x)
7721       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7722     if (attr_y < y)
7723       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7724     else if (attr_y > y)
7725       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7726
7727     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7728
7729     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7730     {
7731       boolean first_horiz = RND(2);
7732       int new_move_dir = MovDir[x][y];
7733
7734       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7735       {
7736         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7737         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7738
7739         return;
7740       }
7741
7742       MovDir[x][y] =
7743         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744       Moving2Blocked(x, y, &newx, &newy);
7745
7746       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7747         return;
7748
7749       MovDir[x][y] =
7750         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7751       Moving2Blocked(x, y, &newx, &newy);
7752
7753       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7754         return;
7755
7756       MovDir[x][y] = old_move_dir;
7757     }
7758   }
7759   else if (move_pattern == MV_WHEN_PUSHED ||
7760            move_pattern == MV_WHEN_DROPPED)
7761   {
7762     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763       MovDir[x][y] = MV_NONE;
7764
7765     MovDelay[x][y] = 0;
7766   }
7767   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7768   {
7769     static int test_xy[7][2] =
7770     {
7771       { 0, -1 },
7772       { -1, 0 },
7773       { +1, 0 },
7774       { 0, +1 },
7775       { 0, -1 },
7776       { -1, 0 },
7777       { +1, 0 },
7778     };
7779     static int test_dir[7] =
7780     {
7781       MV_UP,
7782       MV_LEFT,
7783       MV_RIGHT,
7784       MV_DOWN,
7785       MV_UP,
7786       MV_LEFT,
7787       MV_RIGHT,
7788     };
7789     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7790     int move_preference = -1000000;     /* start with very low preference */
7791     int new_move_dir = MV_NONE;
7792     int start_test = RND(4);
7793     int i;
7794
7795     for (i = 0; i < NUM_DIRECTIONS; i++)
7796     {
7797       int move_dir = test_dir[start_test + i];
7798       int move_dir_preference;
7799
7800       xx = x + test_xy[start_test + i][0];
7801       yy = y + test_xy[start_test + i][1];
7802
7803       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7804           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7805       {
7806         new_move_dir = move_dir;
7807
7808         break;
7809       }
7810
7811       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7812         continue;
7813
7814       move_dir_preference = -1 * RunnerVisit[xx][yy];
7815       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7816         move_dir_preference = PlayerVisit[xx][yy];
7817
7818       if (move_dir_preference > move_preference)
7819       {
7820         /* prefer field that has not been visited for the longest time */
7821         move_preference = move_dir_preference;
7822         new_move_dir = move_dir;
7823       }
7824       else if (move_dir_preference == move_preference &&
7825                move_dir == old_move_dir)
7826       {
7827         /* prefer last direction when all directions are preferred equally */
7828         move_preference = move_dir_preference;
7829         new_move_dir = move_dir;
7830       }
7831     }
7832
7833     MovDir[x][y] = new_move_dir;
7834     if (old_move_dir != new_move_dir)
7835       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7836   }
7837 }
7838
7839 static void TurnRound(int x, int y)
7840 {
7841   int direction = MovDir[x][y];
7842
7843   TurnRoundExt(x, y);
7844
7845   GfxDir[x][y] = MovDir[x][y];
7846
7847   if (direction != MovDir[x][y])
7848     GfxFrame[x][y] = 0;
7849
7850   if (MovDelay[x][y])
7851     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7852
7853   ResetGfxFrame(x, y, FALSE);
7854 }
7855
7856 static boolean JustBeingPushed(int x, int y)
7857 {
7858   int i;
7859
7860   for (i = 0; i < MAX_PLAYERS; i++)
7861   {
7862     struct PlayerInfo *player = &stored_player[i];
7863
7864     if (player->active && player->is_pushing && player->MovPos)
7865     {
7866       int next_jx = player->jx + (player->jx - player->last_jx);
7867       int next_jy = player->jy + (player->jy - player->last_jy);
7868
7869       if (x == next_jx && y == next_jy)
7870         return TRUE;
7871     }
7872   }
7873
7874   return FALSE;
7875 }
7876
7877 void StartMoving(int x, int y)
7878 {
7879   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7880   int element = Feld[x][y];
7881
7882   if (Stop[x][y])
7883     return;
7884
7885   if (MovDelay[x][y] == 0)
7886     GfxAction[x][y] = ACTION_DEFAULT;
7887
7888   if (CAN_FALL(element) && y < lev_fieldy - 1)
7889   {
7890     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7891         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7892       if (JustBeingPushed(x, y))
7893         return;
7894
7895     if (element == EL_QUICKSAND_FULL)
7896     {
7897       if (IS_FREE(x, y + 1))
7898       {
7899         InitMovingField(x, y, MV_DOWN);
7900         started_moving = TRUE;
7901
7902         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7903 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7904         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7905           Store[x][y] = EL_ROCK;
7906 #else
7907         Store[x][y] = EL_ROCK;
7908 #endif
7909
7910         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7911       }
7912       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7913       {
7914         if (!MovDelay[x][y])
7915         {
7916           MovDelay[x][y] = TILEY + 1;
7917
7918           ResetGfxAnimation(x, y);
7919           ResetGfxAnimation(x, y + 1);
7920         }
7921
7922         if (MovDelay[x][y])
7923         {
7924           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7925           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7926
7927           MovDelay[x][y]--;
7928           if (MovDelay[x][y])
7929             return;
7930         }
7931
7932         Feld[x][y] = EL_QUICKSAND_EMPTY;
7933         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7934         Store[x][y + 1] = Store[x][y];
7935         Store[x][y] = 0;
7936
7937         PlayLevelSoundAction(x, y, ACTION_FILLING);
7938       }
7939       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_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_FAST_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_FAST_FULL;
7961         Store[x][y + 1] = Store[x][y];
7962         Store[x][y] = 0;
7963
7964         PlayLevelSoundAction(x, y, ACTION_FILLING);
7965       }
7966     }
7967     else if (element == EL_QUICKSAND_FAST_FULL)
7968     {
7969       if (IS_FREE(x, y + 1))
7970       {
7971         InitMovingField(x, y, MV_DOWN);
7972         started_moving = TRUE;
7973
7974         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7976         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7977           Store[x][y] = EL_ROCK;
7978 #else
7979         Store[x][y] = EL_ROCK;
7980 #endif
7981
7982         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7983       }
7984       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7985       {
7986         if (!MovDelay[x][y])
7987         {
7988           MovDelay[x][y] = TILEY + 1;
7989
7990           ResetGfxAnimation(x, y);
7991           ResetGfxAnimation(x, y + 1);
7992         }
7993
7994         if (MovDelay[x][y])
7995         {
7996           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7997           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7998
7999           MovDelay[x][y]--;
8000           if (MovDelay[x][y])
8001             return;
8002         }
8003
8004         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8005         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8006         Store[x][y + 1] = Store[x][y];
8007         Store[x][y] = 0;
8008
8009         PlayLevelSoundAction(x, y, ACTION_FILLING);
8010       }
8011       else if (Feld[x][y + 1] == EL_QUICKSAND_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_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_FULL;
8033         Store[x][y + 1] = Store[x][y];
8034         Store[x][y] = 0;
8035
8036         PlayLevelSoundAction(x, y, ACTION_FILLING);
8037       }
8038     }
8039     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8040              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041     {
8042       InitMovingField(x, y, MV_DOWN);
8043       started_moving = TRUE;
8044
8045       Feld[x][y] = EL_QUICKSAND_FILLING;
8046       Store[x][y] = element;
8047
8048       PlayLevelSoundAction(x, y, ACTION_FILLING);
8049     }
8050     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8051              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8052     {
8053       InitMovingField(x, y, MV_DOWN);
8054       started_moving = TRUE;
8055
8056       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8057       Store[x][y] = element;
8058
8059       PlayLevelSoundAction(x, y, ACTION_FILLING);
8060     }
8061     else if (element == EL_MAGIC_WALL_FULL)
8062     {
8063       if (IS_FREE(x, y + 1))
8064       {
8065         InitMovingField(x, y, MV_DOWN);
8066         started_moving = TRUE;
8067
8068         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8069         Store[x][y] = EL_CHANGED(Store[x][y]);
8070       }
8071       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8072       {
8073         if (!MovDelay[x][y])
8074           MovDelay[x][y] = TILEY/4 + 1;
8075
8076         if (MovDelay[x][y])
8077         {
8078           MovDelay[x][y]--;
8079           if (MovDelay[x][y])
8080             return;
8081         }
8082
8083         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8084         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8085         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8086         Store[x][y] = 0;
8087       }
8088     }
8089     else if (element == EL_BD_MAGIC_WALL_FULL)
8090     {
8091       if (IS_FREE(x, y + 1))
8092       {
8093         InitMovingField(x, y, MV_DOWN);
8094         started_moving = TRUE;
8095
8096         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8097         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8098       }
8099       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8100       {
8101         if (!MovDelay[x][y])
8102           MovDelay[x][y] = TILEY/4 + 1;
8103
8104         if (MovDelay[x][y])
8105         {
8106           MovDelay[x][y]--;
8107           if (MovDelay[x][y])
8108             return;
8109         }
8110
8111         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8112         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8113         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8114         Store[x][y] = 0;
8115       }
8116     }
8117     else if (element == EL_DC_MAGIC_WALL_FULL)
8118     {
8119       if (IS_FREE(x, y + 1))
8120       {
8121         InitMovingField(x, y, MV_DOWN);
8122         started_moving = TRUE;
8123
8124         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8125         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8126       }
8127       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8128       {
8129         if (!MovDelay[x][y])
8130           MovDelay[x][y] = TILEY/4 + 1;
8131
8132         if (MovDelay[x][y])
8133         {
8134           MovDelay[x][y]--;
8135           if (MovDelay[x][y])
8136             return;
8137         }
8138
8139         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8140         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8141         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8142         Store[x][y] = 0;
8143       }
8144     }
8145     else if ((CAN_PASS_MAGIC_WALL(element) &&
8146               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8147                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8148              (CAN_PASS_DC_MAGIC_WALL(element) &&
8149               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8150
8151     {
8152       InitMovingField(x, y, MV_DOWN);
8153       started_moving = TRUE;
8154
8155       Feld[x][y] =
8156         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8157          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8158          EL_DC_MAGIC_WALL_FILLING);
8159       Store[x][y] = element;
8160     }
8161     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8162     {
8163       SplashAcid(x, y + 1);
8164
8165       InitMovingField(x, y, MV_DOWN);
8166       started_moving = TRUE;
8167
8168       Store[x][y] = EL_ACID;
8169     }
8170     else if (
8171 #if USE_FIX_IMPACT_COLLISION
8172              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8174 #else
8175              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8177 #endif
8178              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8179               CAN_FALL(element) && WasJustFalling[x][y] &&
8180               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8181
8182              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8183               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8184               (Feld[x][y + 1] == EL_BLOCKED)))
8185     {
8186       /* this is needed for a special case not covered by calling "Impact()"
8187          from "ContinueMoving()": if an element moves to a tile directly below
8188          another element which was just falling on that tile (which was empty
8189          in the previous frame), the falling element above would just stop
8190          instead of smashing the element below (in previous version, the above
8191          element was just checked for "moving" instead of "falling", resulting
8192          in incorrect smashes caused by horizontal movement of the above
8193          element; also, the case of the player being the element to smash was
8194          simply not covered here... :-/ ) */
8195
8196       CheckCollision[x][y] = 0;
8197       CheckImpact[x][y] = 0;
8198
8199       Impact(x, y);
8200     }
8201     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8202     {
8203       if (MovDir[x][y] == MV_NONE)
8204       {
8205         InitMovingField(x, y, MV_DOWN);
8206         started_moving = TRUE;
8207       }
8208     }
8209     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8210     {
8211       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8212         MovDir[x][y] = MV_DOWN;
8213
8214       InitMovingField(x, y, MV_DOWN);
8215       started_moving = TRUE;
8216     }
8217     else if (element == EL_AMOEBA_DROP)
8218     {
8219       Feld[x][y] = EL_AMOEBA_GROWING;
8220       Store[x][y] = EL_AMOEBA_WET;
8221     }
8222     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8223               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8224              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8225              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8226     {
8227       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8228                                 (IS_FREE(x - 1, y + 1) ||
8229                                  Feld[x - 1][y + 1] == EL_ACID));
8230       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8231                                 (IS_FREE(x + 1, y + 1) ||
8232                                  Feld[x + 1][y + 1] == EL_ACID));
8233       boolean can_fall_any  = (can_fall_left || can_fall_right);
8234       boolean can_fall_both = (can_fall_left && can_fall_right);
8235       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8236
8237 #if USE_NEW_ALL_SLIPPERY
8238       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8239       {
8240         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8241           can_fall_right = FALSE;
8242         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8243           can_fall_left = FALSE;
8244         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8245           can_fall_right = FALSE;
8246         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8247           can_fall_left = FALSE;
8248
8249         can_fall_any  = (can_fall_left || can_fall_right);
8250         can_fall_both = FALSE;
8251       }
8252 #else
8253       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8254       {
8255         if (slippery_type == SLIPPERY_ONLY_LEFT)
8256           can_fall_right = FALSE;
8257         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8258           can_fall_left = FALSE;
8259         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260           can_fall_right = FALSE;
8261         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262           can_fall_left = FALSE;
8263
8264         can_fall_any  = (can_fall_left || can_fall_right);
8265         can_fall_both = (can_fall_left && can_fall_right);
8266       }
8267 #endif
8268
8269 #if USE_NEW_ALL_SLIPPERY
8270 #else
8271 #if USE_NEW_SP_SLIPPERY
8272       /* !!! better use the same properties as for custom elements here !!! */
8273       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8274                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8275       {
8276         can_fall_right = FALSE;         /* slip down on left side */
8277         can_fall_both = FALSE;
8278       }
8279 #endif
8280 #endif
8281
8282 #if USE_NEW_ALL_SLIPPERY
8283       if (can_fall_both)
8284       {
8285         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8286           can_fall_right = FALSE;       /* slip down on left side */
8287         else
8288           can_fall_left = !(can_fall_right = RND(2));
8289
8290         can_fall_both = FALSE;
8291       }
8292 #else
8293       if (can_fall_both)
8294       {
8295         if (game.emulation == EMU_BOULDERDASH ||
8296             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8297           can_fall_right = FALSE;       /* slip down on left side */
8298         else
8299           can_fall_left = !(can_fall_right = RND(2));
8300
8301         can_fall_both = FALSE;
8302       }
8303 #endif
8304
8305       if (can_fall_any)
8306       {
8307         /* if not determined otherwise, prefer left side for slipping down */
8308         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8309         started_moving = TRUE;
8310       }
8311     }
8312 #if 0
8313     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8314 #else
8315     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8316 #endif
8317     {
8318       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8319       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8320       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8321       int belt_dir = game.belt_dir[belt_nr];
8322
8323       if ((belt_dir == MV_LEFT  && left_is_free) ||
8324           (belt_dir == MV_RIGHT && right_is_free))
8325       {
8326         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8327
8328         InitMovingField(x, y, belt_dir);
8329         started_moving = TRUE;
8330
8331         Pushed[x][y] = TRUE;
8332         Pushed[nextx][y] = TRUE;
8333
8334         GfxAction[x][y] = ACTION_DEFAULT;
8335       }
8336       else
8337       {
8338         MovDir[x][y] = 0;       /* if element was moving, stop it */
8339       }
8340     }
8341   }
8342
8343   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8344 #if 0
8345   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8346 #else
8347   if (CAN_MOVE(element) && !started_moving)
8348 #endif
8349   {
8350     int move_pattern = element_info[element].move_pattern;
8351     int newx, newy;
8352
8353 #if 0
8354 #if DEBUG
8355     if (MovDir[x][y] == MV_NONE)
8356     {
8357       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8358              x, y, element, element_info[element].token_name);
8359       printf("StartMoving(): This should never happen!\n");
8360     }
8361 #endif
8362 #endif
8363
8364     Moving2Blocked(x, y, &newx, &newy);
8365
8366     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8367       return;
8368
8369     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8370         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8371     {
8372       WasJustMoving[x][y] = 0;
8373       CheckCollision[x][y] = 0;
8374
8375       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8376
8377       if (Feld[x][y] != element)        /* element has changed */
8378         return;
8379     }
8380
8381     if (!MovDelay[x][y])        /* start new movement phase */
8382     {
8383       /* all objects that can change their move direction after each step
8384          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8385
8386       if (element != EL_YAMYAM &&
8387           element != EL_DARK_YAMYAM &&
8388           element != EL_PACMAN &&
8389           !(move_pattern & MV_ANY_DIRECTION) &&
8390           move_pattern != MV_TURNING_LEFT &&
8391           move_pattern != MV_TURNING_RIGHT &&
8392           move_pattern != MV_TURNING_LEFT_RIGHT &&
8393           move_pattern != MV_TURNING_RIGHT_LEFT &&
8394           move_pattern != MV_TURNING_RANDOM)
8395       {
8396         TurnRound(x, y);
8397
8398         if (MovDelay[x][y] && (element == EL_BUG ||
8399                                element == EL_SPACESHIP ||
8400                                element == EL_SP_SNIKSNAK ||
8401                                element == EL_SP_ELECTRON ||
8402                                element == EL_MOLE))
8403           TEST_DrawLevelField(x, y);
8404       }
8405     }
8406
8407     if (MovDelay[x][y])         /* wait some time before next movement */
8408     {
8409       MovDelay[x][y]--;
8410
8411       if (element == EL_ROBOT ||
8412           element == EL_YAMYAM ||
8413           element == EL_DARK_YAMYAM)
8414       {
8415         DrawLevelElementAnimationIfNeeded(x, y, element);
8416         PlayLevelSoundAction(x, y, ACTION_WAITING);
8417       }
8418       else if (element == EL_SP_ELECTRON)
8419         DrawLevelElementAnimationIfNeeded(x, y, element);
8420       else if (element == EL_DRAGON)
8421       {
8422         int i;
8423         int dir = MovDir[x][y];
8424         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8425         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8426         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8427                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8428                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8429                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8430         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8431
8432         GfxAction[x][y] = ACTION_ATTACKING;
8433
8434         if (IS_PLAYER(x, y))
8435           DrawPlayerField(x, y);
8436         else
8437           TEST_DrawLevelField(x, y);
8438
8439         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8440
8441         for (i = 1; i <= 3; i++)
8442         {
8443           int xx = x + i * dx;
8444           int yy = y + i * dy;
8445           int sx = SCREENX(xx);
8446           int sy = SCREENY(yy);
8447           int flame_graphic = graphic + (i - 1);
8448
8449           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8450             break;
8451
8452           if (MovDelay[x][y])
8453           {
8454             int flamed = MovingOrBlocked2Element(xx, yy);
8455
8456             /* !!! */
8457 #if 0
8458             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8459               Bang(xx, yy);
8460             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8461               RemoveMovingField(xx, yy);
8462             else
8463               RemoveField(xx, yy);
8464 #else
8465             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8466               Bang(xx, yy);
8467             else
8468               RemoveMovingField(xx, yy);
8469 #endif
8470
8471             ChangeDelay[xx][yy] = 0;
8472
8473             Feld[xx][yy] = EL_FLAMES;
8474
8475             if (IN_SCR_FIELD(sx, sy))
8476             {
8477               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8478               DrawGraphic(sx, sy, flame_graphic, frame);
8479             }
8480           }
8481           else
8482           {
8483             if (Feld[xx][yy] == EL_FLAMES)
8484               Feld[xx][yy] = EL_EMPTY;
8485             TEST_DrawLevelField(xx, yy);
8486           }
8487         }
8488       }
8489
8490       if (MovDelay[x][y])       /* element still has to wait some time */
8491       {
8492         PlayLevelSoundAction(x, y, ACTION_WAITING);
8493
8494         return;
8495       }
8496     }
8497
8498     /* now make next step */
8499
8500     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8501
8502     if (DONT_COLLIDE_WITH(element) &&
8503         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8504         !PLAYER_ENEMY_PROTECTED(newx, newy))
8505     {
8506       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8507
8508       return;
8509     }
8510
8511     else if (CAN_MOVE_INTO_ACID(element) &&
8512              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8513              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8514              (MovDir[x][y] == MV_DOWN ||
8515               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8516     {
8517       SplashAcid(newx, newy);
8518       Store[x][y] = EL_ACID;
8519     }
8520     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8521     {
8522       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8523           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8524           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8525           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8526       {
8527         RemoveField(x, y);
8528         TEST_DrawLevelField(x, y);
8529
8530         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8531         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8532           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8533
8534         local_player->friends_still_needed--;
8535         if (!local_player->friends_still_needed &&
8536             !local_player->GameOver && AllPlayersGone)
8537           PlayerWins(local_player);
8538
8539         return;
8540       }
8541       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8542       {
8543         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8544           TEST_DrawLevelField(newx, newy);
8545         else
8546           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8547       }
8548       else if (!IS_FREE(newx, newy))
8549       {
8550         GfxAction[x][y] = ACTION_WAITING;
8551
8552         if (IS_PLAYER(x, y))
8553           DrawPlayerField(x, y);
8554         else
8555           TEST_DrawLevelField(x, y);
8556
8557         return;
8558       }
8559     }
8560     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8561     {
8562       if (IS_FOOD_PIG(Feld[newx][newy]))
8563       {
8564         if (IS_MOVING(newx, newy))
8565           RemoveMovingField(newx, newy);
8566         else
8567         {
8568           Feld[newx][newy] = EL_EMPTY;
8569           TEST_DrawLevelField(newx, newy);
8570         }
8571
8572         PlayLevelSound(x, y, SND_PIG_DIGGING);
8573       }
8574       else if (!IS_FREE(newx, newy))
8575       {
8576         if (IS_PLAYER(x, y))
8577           DrawPlayerField(x, y);
8578         else
8579           TEST_DrawLevelField(x, y);
8580
8581         return;
8582       }
8583     }
8584     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8585     {
8586       if (Store[x][y] != EL_EMPTY)
8587       {
8588         boolean can_clone = FALSE;
8589         int xx, yy;
8590
8591         /* check if element to clone is still there */
8592         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8593         {
8594           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8595           {
8596             can_clone = TRUE;
8597
8598             break;
8599           }
8600         }
8601
8602         /* cannot clone or target field not free anymore -- do not clone */
8603         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8604           Store[x][y] = EL_EMPTY;
8605       }
8606
8607       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8608       {
8609         if (IS_MV_DIAGONAL(MovDir[x][y]))
8610         {
8611           int diagonal_move_dir = MovDir[x][y];
8612           int stored = Store[x][y];
8613           int change_delay = 8;
8614           int graphic;
8615
8616           /* android is moving diagonally */
8617
8618           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8619
8620           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8621           GfxElement[x][y] = EL_EMC_ANDROID;
8622           GfxAction[x][y] = ACTION_SHRINKING;
8623           GfxDir[x][y] = diagonal_move_dir;
8624           ChangeDelay[x][y] = change_delay;
8625
8626           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8627                                    GfxDir[x][y]);
8628
8629           DrawLevelGraphicAnimation(x, y, graphic);
8630           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8631
8632           if (Feld[newx][newy] == EL_ACID)
8633           {
8634             SplashAcid(newx, newy);
8635
8636             return;
8637           }
8638
8639           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8640
8641           Store[newx][newy] = EL_EMC_ANDROID;
8642           GfxElement[newx][newy] = EL_EMC_ANDROID;
8643           GfxAction[newx][newy] = ACTION_GROWING;
8644           GfxDir[newx][newy] = diagonal_move_dir;
8645           ChangeDelay[newx][newy] = change_delay;
8646
8647           graphic = el_act_dir2img(GfxElement[newx][newy],
8648                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8649
8650           DrawLevelGraphicAnimation(newx, newy, graphic);
8651           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8652
8653           return;
8654         }
8655         else
8656         {
8657           Feld[newx][newy] = EL_EMPTY;
8658           TEST_DrawLevelField(newx, newy);
8659
8660           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8661         }
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665 #if 0
8666         if (IS_PLAYER(x, y))
8667           DrawPlayerField(x, y);
8668         else
8669           TEST_DrawLevelField(x, y);
8670 #endif
8671
8672         return;
8673       }
8674     }
8675     else if (IS_CUSTOM_ELEMENT(element) &&
8676              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8677     {
8678 #if 1
8679       if (!DigFieldByCE(newx, newy, element))
8680         return;
8681 #else
8682       int new_element = Feld[newx][newy];
8683
8684       if (!IS_FREE(newx, newy))
8685       {
8686         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8687                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8688                       ACTION_BREAKING);
8689
8690         /* no element can dig solid indestructible elements */
8691         if (IS_INDESTRUCTIBLE(new_element) &&
8692             !IS_DIGGABLE(new_element) &&
8693             !IS_COLLECTIBLE(new_element))
8694           return;
8695
8696         if (AmoebaNr[newx][newy] &&
8697             (new_element == EL_AMOEBA_FULL ||
8698              new_element == EL_BD_AMOEBA ||
8699              new_element == EL_AMOEBA_GROWING))
8700         {
8701           AmoebaCnt[AmoebaNr[newx][newy]]--;
8702           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8703         }
8704
8705         if (IS_MOVING(newx, newy))
8706           RemoveMovingField(newx, newy);
8707         else
8708         {
8709           RemoveField(newx, newy);
8710           TEST_DrawLevelField(newx, newy);
8711         }
8712
8713         /* if digged element was about to explode, prevent the explosion */
8714         ExplodeField[newx][newy] = EX_TYPE_NONE;
8715
8716         PlayLevelSoundAction(x, y, action);
8717       }
8718
8719       Store[newx][newy] = EL_EMPTY;
8720
8721 #if 1
8722       /* this makes it possible to leave the removed element again */
8723       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8724         Store[newx][newy] = new_element;
8725 #else
8726       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8727       {
8728         int move_leave_element = element_info[element].move_leave_element;
8729
8730         /* this makes it possible to leave the removed element again */
8731         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8732                              new_element : move_leave_element);
8733       }
8734 #endif
8735
8736 #endif
8737
8738       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8739       {
8740         RunnerVisit[x][y] = FrameCounter;
8741         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8742       }
8743     }
8744     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8745     {
8746       if (!IS_FREE(newx, newy))
8747       {
8748         if (IS_PLAYER(x, y))
8749           DrawPlayerField(x, y);
8750         else
8751           TEST_DrawLevelField(x, y);
8752
8753         return;
8754       }
8755       else
8756       {
8757         boolean wanna_flame = !RND(10);
8758         int dx = newx - x, dy = newy - y;
8759         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8760         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8761         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8762                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8763         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8764                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8765
8766         if ((wanna_flame ||
8767              IS_CLASSIC_ENEMY(element1) ||
8768              IS_CLASSIC_ENEMY(element2)) &&
8769             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8770             element1 != EL_FLAMES && element2 != EL_FLAMES)
8771         {
8772           ResetGfxAnimation(x, y);
8773           GfxAction[x][y] = ACTION_ATTACKING;
8774
8775           if (IS_PLAYER(x, y))
8776             DrawPlayerField(x, y);
8777           else
8778             TEST_DrawLevelField(x, y);
8779
8780           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8781
8782           MovDelay[x][y] = 50;
8783
8784           /* !!! */
8785 #if 0
8786           RemoveField(newx, newy);
8787 #endif
8788           Feld[newx][newy] = EL_FLAMES;
8789           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8790           {
8791 #if 0
8792             RemoveField(newx1, newy1);
8793 #endif
8794             Feld[newx1][newy1] = EL_FLAMES;
8795           }
8796           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8797           {
8798 #if 0
8799             RemoveField(newx2, newy2);
8800 #endif
8801             Feld[newx2][newy2] = EL_FLAMES;
8802           }
8803
8804           return;
8805         }
8806       }
8807     }
8808     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8809              Feld[newx][newy] == EL_DIAMOND)
8810     {
8811       if (IS_MOVING(newx, newy))
8812         RemoveMovingField(newx, newy);
8813       else
8814       {
8815         Feld[newx][newy] = EL_EMPTY;
8816         TEST_DrawLevelField(newx, newy);
8817       }
8818
8819       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8820     }
8821     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8822              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8823     {
8824       if (AmoebaNr[newx][newy])
8825       {
8826         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8827         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8828             Feld[newx][newy] == EL_BD_AMOEBA)
8829           AmoebaCnt[AmoebaNr[newx][newy]]--;
8830       }
8831
8832 #if 0
8833       /* !!! test !!! */
8834       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8835       {
8836         RemoveMovingField(newx, newy);
8837       }
8838 #else
8839       if (IS_MOVING(newx, newy))
8840       {
8841         RemoveMovingField(newx, newy);
8842       }
8843 #endif
8844       else
8845       {
8846         Feld[newx][newy] = EL_EMPTY;
8847         TEST_DrawLevelField(newx, newy);
8848       }
8849
8850       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8851     }
8852     else if ((element == EL_PACMAN || element == EL_MOLE)
8853              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8854     {
8855       if (AmoebaNr[newx][newy])
8856       {
8857         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8858         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8859             Feld[newx][newy] == EL_BD_AMOEBA)
8860           AmoebaCnt[AmoebaNr[newx][newy]]--;
8861       }
8862
8863       if (element == EL_MOLE)
8864       {
8865         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8866         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8867
8868         ResetGfxAnimation(x, y);
8869         GfxAction[x][y] = ACTION_DIGGING;
8870         TEST_DrawLevelField(x, y);
8871
8872         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8873
8874         return;                         /* wait for shrinking amoeba */
8875       }
8876       else      /* element == EL_PACMAN */
8877       {
8878         Feld[newx][newy] = EL_EMPTY;
8879         TEST_DrawLevelField(newx, newy);
8880         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8881       }
8882     }
8883     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8884              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8885               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8886     {
8887       /* wait for shrinking amoeba to completely disappear */
8888       return;
8889     }
8890     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8891     {
8892       /* object was running against a wall */
8893
8894       TurnRound(x, y);
8895
8896 #if 0
8897       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8898       if (move_pattern & MV_ANY_DIRECTION &&
8899           move_pattern == MovDir[x][y])
8900       {
8901         int blocking_element =
8902           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8903
8904         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8905                                  MovDir[x][y]);
8906
8907         element = Feld[x][y];   /* element might have changed */
8908       }
8909 #endif
8910
8911       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8912         DrawLevelElementAnimation(x, y, element);
8913
8914       if (DONT_TOUCH(element))
8915         TestIfBadThingTouchesPlayer(x, y);
8916
8917       return;
8918     }
8919
8920     InitMovingField(x, y, MovDir[x][y]);
8921
8922     PlayLevelSoundAction(x, y, ACTION_MOVING);
8923   }
8924
8925   if (MovDir[x][y])
8926     ContinueMoving(x, y);
8927 }
8928
8929 void ContinueMoving(int x, int y)
8930 {
8931   int element = Feld[x][y];
8932   struct ElementInfo *ei = &element_info[element];
8933   int direction = MovDir[x][y];
8934   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8935   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8936   int newx = x + dx, newy = y + dy;
8937   int stored = Store[x][y];
8938   int stored_new = Store[newx][newy];
8939   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8940   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8941   boolean last_line = (newy == lev_fieldy - 1);
8942
8943   MovPos[x][y] += getElementMoveStepsize(x, y);
8944
8945   if (pushed_by_player) /* special case: moving object pushed by player */
8946     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8947
8948   if (ABS(MovPos[x][y]) < TILEX)
8949   {
8950 #if 0
8951     int ee = Feld[x][y];
8952     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8953     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8954
8955     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8956            x, y, ABS(MovPos[x][y]),
8957            ee, gg, ff,
8958            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8959 #endif
8960
8961     TEST_DrawLevelField(x, y);
8962
8963     return;     /* element is still moving */
8964   }
8965
8966   /* element reached destination field */
8967
8968   Feld[x][y] = EL_EMPTY;
8969   Feld[newx][newy] = element;
8970   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8971
8972   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8973   {
8974     element = Feld[newx][newy] = EL_ACID;
8975   }
8976   else if (element == EL_MOLE)
8977   {
8978     Feld[x][y] = EL_SAND;
8979
8980     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8981   }
8982   else if (element == EL_QUICKSAND_FILLING)
8983   {
8984     element = Feld[newx][newy] = get_next_element(element);
8985     Store[newx][newy] = Store[x][y];
8986   }
8987   else if (element == EL_QUICKSAND_EMPTYING)
8988   {
8989     Feld[x][y] = get_next_element(element);
8990     element = Feld[newx][newy] = Store[x][y];
8991   }
8992   else if (element == EL_QUICKSAND_FAST_FILLING)
8993   {
8994     element = Feld[newx][newy] = get_next_element(element);
8995     Store[newx][newy] = Store[x][y];
8996   }
8997   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8998   {
8999     Feld[x][y] = get_next_element(element);
9000     element = Feld[newx][newy] = Store[x][y];
9001   }
9002   else if (element == EL_MAGIC_WALL_FILLING)
9003   {
9004     element = Feld[newx][newy] = get_next_element(element);
9005     if (!game.magic_wall_active)
9006       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9007     Store[newx][newy] = Store[x][y];
9008   }
9009   else if (element == EL_MAGIC_WALL_EMPTYING)
9010   {
9011     Feld[x][y] = get_next_element(element);
9012     if (!game.magic_wall_active)
9013       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9014     element = Feld[newx][newy] = Store[x][y];
9015
9016 #if USE_NEW_CUSTOM_VALUE
9017     InitField(newx, newy, FALSE);
9018 #endif
9019   }
9020   else if (element == EL_BD_MAGIC_WALL_FILLING)
9021   {
9022     element = Feld[newx][newy] = get_next_element(element);
9023     if (!game.magic_wall_active)
9024       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9025     Store[newx][newy] = Store[x][y];
9026   }
9027   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9028   {
9029     Feld[x][y] = get_next_element(element);
9030     if (!game.magic_wall_active)
9031       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9032     element = Feld[newx][newy] = Store[x][y];
9033
9034 #if USE_NEW_CUSTOM_VALUE
9035     InitField(newx, newy, FALSE);
9036 #endif
9037   }
9038   else if (element == EL_DC_MAGIC_WALL_FILLING)
9039   {
9040     element = Feld[newx][newy] = get_next_element(element);
9041     if (!game.magic_wall_active)
9042       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9043     Store[newx][newy] = Store[x][y];
9044   }
9045   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9046   {
9047     Feld[x][y] = get_next_element(element);
9048     if (!game.magic_wall_active)
9049       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9050     element = Feld[newx][newy] = Store[x][y];
9051
9052 #if USE_NEW_CUSTOM_VALUE
9053     InitField(newx, newy, FALSE);
9054 #endif
9055   }
9056   else if (element == EL_AMOEBA_DROPPING)
9057   {
9058     Feld[x][y] = get_next_element(element);
9059     element = Feld[newx][newy] = Store[x][y];
9060   }
9061   else if (element == EL_SOKOBAN_OBJECT)
9062   {
9063     if (Back[x][y])
9064       Feld[x][y] = Back[x][y];
9065
9066     if (Back[newx][newy])
9067       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9068
9069     Back[x][y] = Back[newx][newy] = 0;
9070   }
9071
9072   Store[x][y] = EL_EMPTY;
9073   MovPos[x][y] = 0;
9074   MovDir[x][y] = 0;
9075   MovDelay[x][y] = 0;
9076
9077   MovDelay[newx][newy] = 0;
9078
9079   if (CAN_CHANGE_OR_HAS_ACTION(element))
9080   {
9081     /* copy element change control values to new field */
9082     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9083     ChangePage[newx][newy]  = ChangePage[x][y];
9084     ChangeCount[newx][newy] = ChangeCount[x][y];
9085     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9086   }
9087
9088 #if USE_NEW_CUSTOM_VALUE
9089   CustomValue[newx][newy] = CustomValue[x][y];
9090 #endif
9091
9092   ChangeDelay[x][y] = 0;
9093   ChangePage[x][y] = -1;
9094   ChangeCount[x][y] = 0;
9095   ChangeEvent[x][y] = -1;
9096
9097 #if USE_NEW_CUSTOM_VALUE
9098   CustomValue[x][y] = 0;
9099 #endif
9100
9101   /* copy animation control values to new field */
9102   GfxFrame[newx][newy]  = GfxFrame[x][y];
9103   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9104   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9105   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9106
9107   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9108
9109   /* some elements can leave other elements behind after moving */
9110 #if 1
9111   if (ei->move_leave_element != EL_EMPTY &&
9112       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9113       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9114 #else
9115   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9116       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9117       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9118 #endif
9119   {
9120     int move_leave_element = ei->move_leave_element;
9121
9122 #if 1
9123 #if 1
9124     /* this makes it possible to leave the removed element again */
9125     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9126       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9127 #else
9128     /* this makes it possible to leave the removed element again */
9129     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9130       move_leave_element = stored;
9131 #endif
9132 #else
9133     /* this makes it possible to leave the removed element again */
9134     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9135         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9136       move_leave_element = stored;
9137 #endif
9138
9139     Feld[x][y] = move_leave_element;
9140
9141     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9142       MovDir[x][y] = direction;
9143
9144     InitField(x, y, FALSE);
9145
9146     if (GFX_CRUMBLED(Feld[x][y]))
9147       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9148
9149     if (ELEM_IS_PLAYER(move_leave_element))
9150       RelocatePlayer(x, y, move_leave_element);
9151   }
9152
9153   /* do this after checking for left-behind element */
9154   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9155
9156   if (!CAN_MOVE(element) ||
9157       (CAN_FALL(element) && direction == MV_DOWN &&
9158        (element == EL_SPRING ||
9159         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9160         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9161     GfxDir[x][y] = MovDir[newx][newy] = 0;
9162
9163   TEST_DrawLevelField(x, y);
9164   TEST_DrawLevelField(newx, newy);
9165
9166   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9167
9168   /* prevent pushed element from moving on in pushed direction */
9169   if (pushed_by_player && CAN_MOVE(element) &&
9170       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9171       !(element_info[element].move_pattern & direction))
9172     TurnRound(newx, newy);
9173
9174   /* prevent elements on conveyor belt from moving on in last direction */
9175   if (pushed_by_conveyor && CAN_FALL(element) &&
9176       direction & MV_HORIZONTAL)
9177     MovDir[newx][newy] = 0;
9178
9179   if (!pushed_by_player)
9180   {
9181     int nextx = newx + dx, nexty = newy + dy;
9182     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9183
9184     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9185
9186     if (CAN_FALL(element) && direction == MV_DOWN)
9187       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9188
9189     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9190       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9191
9192 #if USE_FIX_IMPACT_COLLISION
9193     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9194       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9195 #endif
9196   }
9197
9198   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9199   {
9200     TestIfBadThingTouchesPlayer(newx, newy);
9201     TestIfBadThingTouchesFriend(newx, newy);
9202
9203     if (!IS_CUSTOM_ELEMENT(element))
9204       TestIfBadThingTouchesOtherBadThing(newx, newy);
9205   }
9206   else if (element == EL_PENGUIN)
9207     TestIfFriendTouchesBadThing(newx, newy);
9208
9209   if (DONT_GET_HIT_BY(element))
9210   {
9211     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9212   }
9213
9214   /* give the player one last chance (one more frame) to move away */
9215   if (CAN_FALL(element) && direction == MV_DOWN &&
9216       (last_line || (!IS_FREE(x, newy + 1) &&
9217                      (!IS_PLAYER(x, newy + 1) ||
9218                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9219     Impact(x, newy);
9220
9221   if (pushed_by_player && !game.use_change_when_pushing_bug)
9222   {
9223     int push_side = MV_DIR_OPPOSITE(direction);
9224     struct PlayerInfo *player = PLAYERINFO(x, y);
9225
9226     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9227                                player->index_bit, push_side);
9228     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9229                                         player->index_bit, push_side);
9230   }
9231
9232   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9233     MovDelay[newx][newy] = 1;
9234
9235   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9236
9237   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9238
9239 #if 0
9240   if (ChangePage[newx][newy] != -1)             /* delayed change */
9241   {
9242     int page = ChangePage[newx][newy];
9243     struct ElementChangeInfo *change = &ei->change_page[page];
9244
9245     ChangePage[newx][newy] = -1;
9246
9247     if (change->can_change)
9248     {
9249       if (ChangeElement(newx, newy, element, page))
9250       {
9251         if (change->post_change_function)
9252           change->post_change_function(newx, newy);
9253       }
9254     }
9255
9256     if (change->has_action)
9257       ExecuteCustomElementAction(newx, newy, element, page);
9258   }
9259 #endif
9260
9261   TestIfElementHitsCustomElement(newx, newy, direction);
9262   TestIfPlayerTouchesCustomElement(newx, newy);
9263   TestIfElementTouchesCustomElement(newx, newy);
9264
9265   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9266       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9267     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9268                              MV_DIR_OPPOSITE(direction));
9269 }
9270
9271 int AmoebeNachbarNr(int ax, int ay)
9272 {
9273   int i;
9274   int element = Feld[ax][ay];
9275   int group_nr = 0;
9276   static int xy[4][2] =
9277   {
9278     { 0, -1 },
9279     { -1, 0 },
9280     { +1, 0 },
9281     { 0, +1 }
9282   };
9283
9284   for (i = 0; i < NUM_DIRECTIONS; i++)
9285   {
9286     int x = ax + xy[i][0];
9287     int y = ay + xy[i][1];
9288
9289     if (!IN_LEV_FIELD(x, y))
9290       continue;
9291
9292     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9293       group_nr = AmoebaNr[x][y];
9294   }
9295
9296   return group_nr;
9297 }
9298
9299 void AmoebenVereinigen(int ax, int ay)
9300 {
9301   int i, x, y, xx, yy;
9302   int new_group_nr = AmoebaNr[ax][ay];
9303   static int xy[4][2] =
9304   {
9305     { 0, -1 },
9306     { -1, 0 },
9307     { +1, 0 },
9308     { 0, +1 }
9309   };
9310
9311   if (new_group_nr == 0)
9312     return;
9313
9314   for (i = 0; i < NUM_DIRECTIONS; i++)
9315   {
9316     x = ax + xy[i][0];
9317     y = ay + xy[i][1];
9318
9319     if (!IN_LEV_FIELD(x, y))
9320       continue;
9321
9322     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9323          Feld[x][y] == EL_BD_AMOEBA ||
9324          Feld[x][y] == EL_AMOEBA_DEAD) &&
9325         AmoebaNr[x][y] != new_group_nr)
9326     {
9327       int old_group_nr = AmoebaNr[x][y];
9328
9329       if (old_group_nr == 0)
9330         return;
9331
9332       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9333       AmoebaCnt[old_group_nr] = 0;
9334       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9335       AmoebaCnt2[old_group_nr] = 0;
9336
9337       SCAN_PLAYFIELD(xx, yy)
9338       {
9339         if (AmoebaNr[xx][yy] == old_group_nr)
9340           AmoebaNr[xx][yy] = new_group_nr;
9341       }
9342     }
9343   }
9344 }
9345
9346 void AmoebeUmwandeln(int ax, int ay)
9347 {
9348   int i, x, y;
9349
9350   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9351   {
9352     int group_nr = AmoebaNr[ax][ay];
9353
9354 #ifdef DEBUG
9355     if (group_nr == 0)
9356     {
9357       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9358       printf("AmoebeUmwandeln(): This should never happen!\n");
9359       return;
9360     }
9361 #endif
9362
9363     SCAN_PLAYFIELD(x, y)
9364     {
9365       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9366       {
9367         AmoebaNr[x][y] = 0;
9368         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9369       }
9370     }
9371
9372     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9373                             SND_AMOEBA_TURNING_TO_GEM :
9374                             SND_AMOEBA_TURNING_TO_ROCK));
9375     Bang(ax, ay);
9376   }
9377   else
9378   {
9379     static int xy[4][2] =
9380     {
9381       { 0, -1 },
9382       { -1, 0 },
9383       { +1, 0 },
9384       { 0, +1 }
9385     };
9386
9387     for (i = 0; i < NUM_DIRECTIONS; i++)
9388     {
9389       x = ax + xy[i][0];
9390       y = ay + xy[i][1];
9391
9392       if (!IN_LEV_FIELD(x, y))
9393         continue;
9394
9395       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9396       {
9397         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9398                               SND_AMOEBA_TURNING_TO_GEM :
9399                               SND_AMOEBA_TURNING_TO_ROCK));
9400         Bang(x, y);
9401       }
9402     }
9403   }
9404 }
9405
9406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9407 {
9408   int x, y;
9409   int group_nr = AmoebaNr[ax][ay];
9410   boolean done = FALSE;
9411
9412 #ifdef DEBUG
9413   if (group_nr == 0)
9414   {
9415     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9416     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9417     return;
9418   }
9419 #endif
9420
9421   SCAN_PLAYFIELD(x, y)
9422   {
9423     if (AmoebaNr[x][y] == group_nr &&
9424         (Feld[x][y] == EL_AMOEBA_DEAD ||
9425          Feld[x][y] == EL_BD_AMOEBA ||
9426          Feld[x][y] == EL_AMOEBA_GROWING))
9427     {
9428       AmoebaNr[x][y] = 0;
9429       Feld[x][y] = new_element;
9430       InitField(x, y, FALSE);
9431       TEST_DrawLevelField(x, y);
9432       done = TRUE;
9433     }
9434   }
9435
9436   if (done)
9437     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9438                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9439                             SND_BD_AMOEBA_TURNING_TO_GEM));
9440 }
9441
9442 void AmoebeWaechst(int x, int y)
9443 {
9444   static unsigned long sound_delay = 0;
9445   static unsigned long sound_delay_value = 0;
9446
9447   if (!MovDelay[x][y])          /* start new growing cycle */
9448   {
9449     MovDelay[x][y] = 7;
9450
9451     if (DelayReached(&sound_delay, sound_delay_value))
9452     {
9453       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9454       sound_delay_value = 30;
9455     }
9456   }
9457
9458   if (MovDelay[x][y])           /* wait some time before growing bigger */
9459   {
9460     MovDelay[x][y]--;
9461     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9462     {
9463       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9464                                            6 - MovDelay[x][y]);
9465
9466       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9467     }
9468
9469     if (!MovDelay[x][y])
9470     {
9471       Feld[x][y] = Store[x][y];
9472       Store[x][y] = 0;
9473       TEST_DrawLevelField(x, y);
9474     }
9475   }
9476 }
9477
9478 void AmoebaDisappearing(int x, int y)
9479 {
9480   static unsigned long sound_delay = 0;
9481   static unsigned long sound_delay_value = 0;
9482
9483   if (!MovDelay[x][y])          /* start new shrinking cycle */
9484   {
9485     MovDelay[x][y] = 7;
9486
9487     if (DelayReached(&sound_delay, sound_delay_value))
9488       sound_delay_value = 30;
9489   }
9490
9491   if (MovDelay[x][y])           /* wait some time before shrinking */
9492   {
9493     MovDelay[x][y]--;
9494     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9495     {
9496       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9497                                            6 - MovDelay[x][y]);
9498
9499       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9500     }
9501
9502     if (!MovDelay[x][y])
9503     {
9504       Feld[x][y] = EL_EMPTY;
9505       TEST_DrawLevelField(x, y);
9506
9507       /* don't let mole enter this field in this cycle;
9508          (give priority to objects falling to this field from above) */
9509       Stop[x][y] = TRUE;
9510     }
9511   }
9512 }
9513
9514 void AmoebeAbleger(int ax, int ay)
9515 {
9516   int i;
9517   int element = Feld[ax][ay];
9518   int graphic = el2img(element);
9519   int newax = ax, neway = ay;
9520   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9521   static int xy[4][2] =
9522   {
9523     { 0, -1 },
9524     { -1, 0 },
9525     { +1, 0 },
9526     { 0, +1 }
9527   };
9528
9529   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9530   {
9531     Feld[ax][ay] = EL_AMOEBA_DEAD;
9532     TEST_DrawLevelField(ax, ay);
9533     return;
9534   }
9535
9536   if (IS_ANIMATED(graphic))
9537     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9538
9539   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9540     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9541
9542   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9543   {
9544     MovDelay[ax][ay]--;
9545     if (MovDelay[ax][ay])
9546       return;
9547   }
9548
9549   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9550   {
9551     int start = RND(4);
9552     int x = ax + xy[start][0];
9553     int y = ay + xy[start][1];
9554
9555     if (!IN_LEV_FIELD(x, y))
9556       return;
9557
9558     if (IS_FREE(x, y) ||
9559         CAN_GROW_INTO(Feld[x][y]) ||
9560         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9561         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9562     {
9563       newax = x;
9564       neway = y;
9565     }
9566
9567     if (newax == ax && neway == ay)
9568       return;
9569   }
9570   else                          /* normal or "filled" (BD style) amoeba */
9571   {
9572     int start = RND(4);
9573     boolean waiting_for_player = FALSE;
9574
9575     for (i = 0; i < NUM_DIRECTIONS; i++)
9576     {
9577       int j = (start + i) % 4;
9578       int x = ax + xy[j][0];
9579       int y = ay + xy[j][1];
9580
9581       if (!IN_LEV_FIELD(x, y))
9582         continue;
9583
9584       if (IS_FREE(x, y) ||
9585           CAN_GROW_INTO(Feld[x][y]) ||
9586           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9587           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9588       {
9589         newax = x;
9590         neway = y;
9591         break;
9592       }
9593       else if (IS_PLAYER(x, y))
9594         waiting_for_player = TRUE;
9595     }
9596
9597     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9598     {
9599       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9600       {
9601         Feld[ax][ay] = EL_AMOEBA_DEAD;
9602         TEST_DrawLevelField(ax, ay);
9603         AmoebaCnt[AmoebaNr[ax][ay]]--;
9604
9605         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9606         {
9607           if (element == EL_AMOEBA_FULL)
9608             AmoebeUmwandeln(ax, ay);
9609           else if (element == EL_BD_AMOEBA)
9610             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9611         }
9612       }
9613       return;
9614     }
9615     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9616     {
9617       /* amoeba gets larger by growing in some direction */
9618
9619       int new_group_nr = AmoebaNr[ax][ay];
9620
9621 #ifdef DEBUG
9622   if (new_group_nr == 0)
9623   {
9624     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9625     printf("AmoebeAbleger(): This should never happen!\n");
9626     return;
9627   }
9628 #endif
9629
9630       AmoebaNr[newax][neway] = new_group_nr;
9631       AmoebaCnt[new_group_nr]++;
9632       AmoebaCnt2[new_group_nr]++;
9633
9634       /* if amoeba touches other amoeba(s) after growing, unify them */
9635       AmoebenVereinigen(newax, neway);
9636
9637       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9638       {
9639         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9640         return;
9641       }
9642     }
9643   }
9644
9645   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9646       (neway == lev_fieldy - 1 && newax != ax))
9647   {
9648     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9649     Store[newax][neway] = element;
9650   }
9651   else if (neway == ay || element == EL_EMC_DRIPPER)
9652   {
9653     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9654
9655     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9656   }
9657   else
9658   {
9659     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9660     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9661     Store[ax][ay] = EL_AMOEBA_DROP;
9662     ContinueMoving(ax, ay);
9663     return;
9664   }
9665
9666   TEST_DrawLevelField(newax, neway);
9667 }
9668
9669 void Life(int ax, int ay)
9670 {
9671   int x1, y1, x2, y2;
9672   int life_time = 40;
9673   int element = Feld[ax][ay];
9674   int graphic = el2img(element);
9675   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9676                          level.biomaze);
9677   boolean changed = FALSE;
9678
9679   if (IS_ANIMATED(graphic))
9680     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9681
9682   if (Stop[ax][ay])
9683     return;
9684
9685   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9686     MovDelay[ax][ay] = life_time;
9687
9688   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9689   {
9690     MovDelay[ax][ay]--;
9691     if (MovDelay[ax][ay])
9692       return;
9693   }
9694
9695   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9696   {
9697     int xx = ax+x1, yy = ay+y1;
9698     int nachbarn = 0;
9699
9700     if (!IN_LEV_FIELD(xx, yy))
9701       continue;
9702
9703     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9704     {
9705       int x = xx+x2, y = yy+y2;
9706
9707       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9708         continue;
9709
9710       if (((Feld[x][y] == element ||
9711             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9712            !Stop[x][y]) ||
9713           (IS_FREE(x, y) && Stop[x][y]))
9714         nachbarn++;
9715     }
9716
9717     if (xx == ax && yy == ay)           /* field in the middle */
9718     {
9719       if (nachbarn < life_parameter[0] ||
9720           nachbarn > life_parameter[1])
9721       {
9722         Feld[xx][yy] = EL_EMPTY;
9723         if (!Stop[xx][yy])
9724           TEST_DrawLevelField(xx, yy);
9725         Stop[xx][yy] = TRUE;
9726         changed = TRUE;
9727       }
9728     }
9729     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9730     {                                   /* free border field */
9731       if (nachbarn >= life_parameter[2] &&
9732           nachbarn <= life_parameter[3])
9733       {
9734         Feld[xx][yy] = element;
9735         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9736         if (!Stop[xx][yy])
9737           TEST_DrawLevelField(xx, yy);
9738         Stop[xx][yy] = TRUE;
9739         changed = TRUE;
9740       }
9741     }
9742   }
9743
9744   if (changed)
9745     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9746                    SND_GAME_OF_LIFE_GROWING);
9747 }
9748
9749 static void InitRobotWheel(int x, int y)
9750 {
9751   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9752 }
9753
9754 static void RunRobotWheel(int x, int y)
9755 {
9756   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9757 }
9758
9759 static void StopRobotWheel(int x, int y)
9760 {
9761   if (ZX == x && ZY == y)
9762   {
9763     ZX = ZY = -1;
9764
9765     game.robot_wheel_active = FALSE;
9766   }
9767 }
9768
9769 static void InitTimegateWheel(int x, int y)
9770 {
9771   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9772 }
9773
9774 static void RunTimegateWheel(int x, int y)
9775 {
9776   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9777 }
9778
9779 static void InitMagicBallDelay(int x, int y)
9780 {
9781 #if 1
9782   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9783 #else
9784   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9785 #endif
9786 }
9787
9788 static void ActivateMagicBall(int bx, int by)
9789 {
9790   int x, y;
9791
9792   if (level.ball_random)
9793   {
9794     int pos_border = RND(8);    /* select one of the eight border elements */
9795     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9796     int xx = pos_content % 3;
9797     int yy = pos_content / 3;
9798
9799     x = bx - 1 + xx;
9800     y = by - 1 + yy;
9801
9802     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9803       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9804   }
9805   else
9806   {
9807     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9808     {
9809       int xx = x - bx + 1;
9810       int yy = y - by + 1;
9811
9812       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9813         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9814     }
9815   }
9816
9817   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9818 }
9819
9820 void CheckExit(int x, int y)
9821 {
9822   if (local_player->gems_still_needed > 0 ||
9823       local_player->sokobanfields_still_needed > 0 ||
9824       local_player->lights_still_needed > 0)
9825   {
9826     int element = Feld[x][y];
9827     int graphic = el2img(element);
9828
9829     if (IS_ANIMATED(graphic))
9830       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9831
9832     return;
9833   }
9834
9835   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9836     return;
9837
9838   Feld[x][y] = EL_EXIT_OPENING;
9839
9840   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9841 }
9842
9843 void CheckExitEM(int x, int y)
9844 {
9845   if (local_player->gems_still_needed > 0 ||
9846       local_player->sokobanfields_still_needed > 0 ||
9847       local_player->lights_still_needed > 0)
9848   {
9849     int element = Feld[x][y];
9850     int graphic = el2img(element);
9851
9852     if (IS_ANIMATED(graphic))
9853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9854
9855     return;
9856   }
9857
9858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9859     return;
9860
9861   Feld[x][y] = EL_EM_EXIT_OPENING;
9862
9863   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9864 }
9865
9866 void CheckExitSteel(int x, int y)
9867 {
9868   if (local_player->gems_still_needed > 0 ||
9869       local_player->sokobanfields_still_needed > 0 ||
9870       local_player->lights_still_needed > 0)
9871   {
9872     int element = Feld[x][y];
9873     int graphic = el2img(element);
9874
9875     if (IS_ANIMATED(graphic))
9876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9877
9878     return;
9879   }
9880
9881   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9882     return;
9883
9884   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9885
9886   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9887 }
9888
9889 void CheckExitSteelEM(int x, int y)
9890 {
9891   if (local_player->gems_still_needed > 0 ||
9892       local_player->sokobanfields_still_needed > 0 ||
9893       local_player->lights_still_needed > 0)
9894   {
9895     int element = Feld[x][y];
9896     int graphic = el2img(element);
9897
9898     if (IS_ANIMATED(graphic))
9899       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9900
9901     return;
9902   }
9903
9904   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9905     return;
9906
9907   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9908
9909   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9910 }
9911
9912 void CheckExitSP(int x, int y)
9913 {
9914   if (local_player->gems_still_needed > 0)
9915   {
9916     int element = Feld[x][y];
9917     int graphic = el2img(element);
9918
9919     if (IS_ANIMATED(graphic))
9920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921
9922     return;
9923   }
9924
9925   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9926     return;
9927
9928   Feld[x][y] = EL_SP_EXIT_OPENING;
9929
9930   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9931 }
9932
9933 static void CloseAllOpenTimegates()
9934 {
9935   int x, y;
9936
9937   SCAN_PLAYFIELD(x, y)
9938   {
9939     int element = Feld[x][y];
9940
9941     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9942     {
9943       Feld[x][y] = EL_TIMEGATE_CLOSING;
9944
9945       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9946     }
9947   }
9948 }
9949
9950 void DrawTwinkleOnField(int x, int y)
9951 {
9952   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9953     return;
9954
9955   if (Feld[x][y] == EL_BD_DIAMOND)
9956     return;
9957
9958   if (MovDelay[x][y] == 0)      /* next animation frame */
9959     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9960
9961   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9962   {
9963     MovDelay[x][y]--;
9964
9965     DrawLevelElementAnimation(x, y, Feld[x][y]);
9966
9967     if (MovDelay[x][y] != 0)
9968     {
9969       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9970                                            10 - MovDelay[x][y]);
9971
9972       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9973     }
9974   }
9975 }
9976
9977 void MauerWaechst(int x, int y)
9978 {
9979   int delay = 6;
9980
9981   if (!MovDelay[x][y])          /* next animation frame */
9982     MovDelay[x][y] = 3 * delay;
9983
9984   if (MovDelay[x][y])           /* wait some time before next frame */
9985   {
9986     MovDelay[x][y]--;
9987
9988     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9989     {
9990       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9991       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9992
9993       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9994     }
9995
9996     if (!MovDelay[x][y])
9997     {
9998       if (MovDir[x][y] == MV_LEFT)
9999       {
10000         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10001           TEST_DrawLevelField(x - 1, y);
10002       }
10003       else if (MovDir[x][y] == MV_RIGHT)
10004       {
10005         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10006           TEST_DrawLevelField(x + 1, y);
10007       }
10008       else if (MovDir[x][y] == MV_UP)
10009       {
10010         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10011           TEST_DrawLevelField(x, y - 1);
10012       }
10013       else
10014       {
10015         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10016           TEST_DrawLevelField(x, y + 1);
10017       }
10018
10019       Feld[x][y] = Store[x][y];
10020       Store[x][y] = 0;
10021       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10022       TEST_DrawLevelField(x, y);
10023     }
10024   }
10025 }
10026
10027 void MauerAbleger(int ax, int ay)
10028 {
10029   int element = Feld[ax][ay];
10030   int graphic = el2img(element);
10031   boolean oben_frei = FALSE, unten_frei = FALSE;
10032   boolean links_frei = FALSE, rechts_frei = FALSE;
10033   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10034   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10035   boolean new_wall = FALSE;
10036
10037   if (IS_ANIMATED(graphic))
10038     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10039
10040   if (!MovDelay[ax][ay])        /* start building new wall */
10041     MovDelay[ax][ay] = 6;
10042
10043   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10044   {
10045     MovDelay[ax][ay]--;
10046     if (MovDelay[ax][ay])
10047       return;
10048   }
10049
10050   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10051     oben_frei = TRUE;
10052   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10053     unten_frei = TRUE;
10054   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10055     links_frei = TRUE;
10056   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10057     rechts_frei = TRUE;
10058
10059   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10060       element == EL_EXPANDABLE_WALL_ANY)
10061   {
10062     if (oben_frei)
10063     {
10064       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10065       Store[ax][ay-1] = element;
10066       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10067       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10068         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10069                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10070       new_wall = TRUE;
10071     }
10072     if (unten_frei)
10073     {
10074       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10075       Store[ax][ay+1] = element;
10076       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10078         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10079                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10080       new_wall = TRUE;
10081     }
10082   }
10083
10084   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10085       element == EL_EXPANDABLE_WALL_ANY ||
10086       element == EL_EXPANDABLE_WALL ||
10087       element == EL_BD_EXPANDABLE_WALL)
10088   {
10089     if (links_frei)
10090     {
10091       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10092       Store[ax-1][ay] = element;
10093       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10094       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10095         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10096                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10097       new_wall = TRUE;
10098     }
10099
10100     if (rechts_frei)
10101     {
10102       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10103       Store[ax+1][ay] = element;
10104       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10105       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10106         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10107                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10108       new_wall = TRUE;
10109     }
10110   }
10111
10112   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10113     TEST_DrawLevelField(ax, ay);
10114
10115   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10116     oben_massiv = TRUE;
10117   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10118     unten_massiv = TRUE;
10119   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10120     links_massiv = TRUE;
10121   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10122     rechts_massiv = TRUE;
10123
10124   if (((oben_massiv && unten_massiv) ||
10125        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126        element == EL_EXPANDABLE_WALL) &&
10127       ((links_massiv && rechts_massiv) ||
10128        element == EL_EXPANDABLE_WALL_VERTICAL))
10129     Feld[ax][ay] = EL_WALL;
10130
10131   if (new_wall)
10132     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10133 }
10134
10135 void MauerAblegerStahl(int ax, int ay)
10136 {
10137   int element = Feld[ax][ay];
10138   int graphic = el2img(element);
10139   boolean oben_frei = FALSE, unten_frei = FALSE;
10140   boolean links_frei = FALSE, rechts_frei = FALSE;
10141   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10142   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10143   boolean new_wall = FALSE;
10144
10145   if (IS_ANIMATED(graphic))
10146     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10147
10148   if (!MovDelay[ax][ay])        /* start building new wall */
10149     MovDelay[ax][ay] = 6;
10150
10151   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10152   {
10153     MovDelay[ax][ay]--;
10154     if (MovDelay[ax][ay])
10155       return;
10156   }
10157
10158   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10159     oben_frei = TRUE;
10160   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10161     unten_frei = TRUE;
10162   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10163     links_frei = TRUE;
10164   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10165     rechts_frei = TRUE;
10166
10167   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10168       element == EL_EXPANDABLE_STEELWALL_ANY)
10169   {
10170     if (oben_frei)
10171     {
10172       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10173       Store[ax][ay-1] = element;
10174       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10175       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10176         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10177                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10178       new_wall = TRUE;
10179     }
10180     if (unten_frei)
10181     {
10182       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10183       Store[ax][ay+1] = element;
10184       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10185       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10186         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10187                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10188       new_wall = TRUE;
10189     }
10190   }
10191
10192   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10193       element == EL_EXPANDABLE_STEELWALL_ANY)
10194   {
10195     if (links_frei)
10196     {
10197       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10198       Store[ax-1][ay] = element;
10199       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10200       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10201         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10202                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10203       new_wall = TRUE;
10204     }
10205
10206     if (rechts_frei)
10207     {
10208       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10209       Store[ax+1][ay] = element;
10210       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10211       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10212         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10213                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10214       new_wall = TRUE;
10215     }
10216   }
10217
10218   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10219     oben_massiv = TRUE;
10220   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10221     unten_massiv = TRUE;
10222   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10223     links_massiv = TRUE;
10224   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10225     rechts_massiv = TRUE;
10226
10227   if (((oben_massiv && unten_massiv) ||
10228        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10229       ((links_massiv && rechts_massiv) ||
10230        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10231     Feld[ax][ay] = EL_STEELWALL;
10232
10233   if (new_wall)
10234     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10235 }
10236
10237 void CheckForDragon(int x, int y)
10238 {
10239   int i, j;
10240   boolean dragon_found = FALSE;
10241   static int xy[4][2] =
10242   {
10243     { 0, -1 },
10244     { -1, 0 },
10245     { +1, 0 },
10246     { 0, +1 }
10247   };
10248
10249   for (i = 0; i < NUM_DIRECTIONS; i++)
10250   {
10251     for (j = 0; j < 4; j++)
10252     {
10253       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10254
10255       if (IN_LEV_FIELD(xx, yy) &&
10256           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10257       {
10258         if (Feld[xx][yy] == EL_DRAGON)
10259           dragon_found = TRUE;
10260       }
10261       else
10262         break;
10263     }
10264   }
10265
10266   if (!dragon_found)
10267   {
10268     for (i = 0; i < NUM_DIRECTIONS; i++)
10269     {
10270       for (j = 0; j < 3; j++)
10271       {
10272         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10273   
10274         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10275         {
10276           Feld[xx][yy] = EL_EMPTY;
10277           TEST_DrawLevelField(xx, yy);
10278         }
10279         else
10280           break;
10281       }
10282     }
10283   }
10284 }
10285
10286 static void InitBuggyBase(int x, int y)
10287 {
10288   int element = Feld[x][y];
10289   int activating_delay = FRAMES_PER_SECOND / 4;
10290
10291   ChangeDelay[x][y] =
10292     (element == EL_SP_BUGGY_BASE ?
10293      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10294      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10295      activating_delay :
10296      element == EL_SP_BUGGY_BASE_ACTIVE ?
10297      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10298 }
10299
10300 static void WarnBuggyBase(int x, int y)
10301 {
10302   int i;
10303   static int xy[4][2] =
10304   {
10305     { 0, -1 },
10306     { -1, 0 },
10307     { +1, 0 },
10308     { 0, +1 }
10309   };
10310
10311   for (i = 0; i < NUM_DIRECTIONS; i++)
10312   {
10313     int xx = x + xy[i][0];
10314     int yy = y + xy[i][1];
10315
10316     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10317     {
10318       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10319
10320       break;
10321     }
10322   }
10323 }
10324
10325 static void InitTrap(int x, int y)
10326 {
10327   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10328 }
10329
10330 static void ActivateTrap(int x, int y)
10331 {
10332   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10333 }
10334
10335 static void ChangeActiveTrap(int x, int y)
10336 {
10337   int graphic = IMG_TRAP_ACTIVE;
10338
10339   /* if new animation frame was drawn, correct crumbled sand border */
10340   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10341     TEST_DrawLevelFieldCrumbledSand(x, y);
10342 }
10343
10344 static int getSpecialActionElement(int element, int number, int base_element)
10345 {
10346   return (element != EL_EMPTY ? element :
10347           number != -1 ? base_element + number - 1 :
10348           EL_EMPTY);
10349 }
10350
10351 static int getModifiedActionNumber(int value_old, int operator, int operand,
10352                                    int value_min, int value_max)
10353 {
10354   int value_new = (operator == CA_MODE_SET      ? operand :
10355                    operator == CA_MODE_ADD      ? value_old + operand :
10356                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10357                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10358                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10359                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10360                    value_old);
10361
10362   return (value_new < value_min ? value_min :
10363           value_new > value_max ? value_max :
10364           value_new);
10365 }
10366
10367 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10368 {
10369   struct ElementInfo *ei = &element_info[element];
10370   struct ElementChangeInfo *change = &ei->change_page[page];
10371   int target_element = change->target_element;
10372   int action_type = change->action_type;
10373   int action_mode = change->action_mode;
10374   int action_arg = change->action_arg;
10375   int action_element = change->action_element;
10376   int i;
10377
10378   if (!change->has_action)
10379     return;
10380
10381   /* ---------- determine action paramater values -------------------------- */
10382
10383   int level_time_value =
10384     (level.time > 0 ? TimeLeft :
10385      TimePlayed);
10386
10387   int action_arg_element_raw =
10388     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10389      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10390      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10391      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10392      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10393      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10394      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10395      EL_EMPTY);
10396   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10397
10398 #if 0
10399   if (action_arg_element_raw == EL_GROUP_START)
10400     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10401 #endif
10402
10403   int action_arg_direction =
10404     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10405      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10406      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10407      change->actual_trigger_side :
10408      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10409      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10410      MV_NONE);
10411
10412   int action_arg_number_min =
10413     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10414      CA_ARG_MIN);
10415
10416   int action_arg_number_max =
10417     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10418      action_type == CA_SET_LEVEL_GEMS ? 999 :
10419      action_type == CA_SET_LEVEL_TIME ? 9999 :
10420      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10421      action_type == CA_SET_CE_VALUE ? 9999 :
10422      action_type == CA_SET_CE_SCORE ? 9999 :
10423      CA_ARG_MAX);
10424
10425   int action_arg_number_reset =
10426     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10427      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10428      action_type == CA_SET_LEVEL_TIME ? level.time :
10429      action_type == CA_SET_LEVEL_SCORE ? 0 :
10430 #if USE_NEW_CUSTOM_VALUE
10431      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10432 #else
10433      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10434 #endif
10435      action_type == CA_SET_CE_SCORE ? 0 :
10436      0);
10437
10438   int action_arg_number =
10439     (action_arg <= CA_ARG_MAX ? action_arg :
10440      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10441      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10442      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10443      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10444      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10445      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10446 #if USE_NEW_CUSTOM_VALUE
10447      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10448 #else
10449      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10450 #endif
10451      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10452      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10453      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10454      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10455      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10456      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10457      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10458      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10459      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10460      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10461      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10462      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10463      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10464      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10465      -1);
10466
10467   int action_arg_number_old =
10468     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10469      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10470      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10471      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10472      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10473      0);
10474
10475   int action_arg_number_new =
10476     getModifiedActionNumber(action_arg_number_old,
10477                             action_mode, action_arg_number,
10478                             action_arg_number_min, action_arg_number_max);
10479
10480 #if 1
10481   int trigger_player_bits =
10482     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10483      change->actual_trigger_player_bits : change->trigger_player);
10484 #else
10485   int trigger_player_bits =
10486     (change->actual_trigger_player >= EL_PLAYER_1 &&
10487      change->actual_trigger_player <= EL_PLAYER_4 ?
10488      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10489      PLAYER_BITS_ANY);
10490 #endif
10491
10492   int action_arg_player_bits =
10493     (action_arg >= CA_ARG_PLAYER_1 &&
10494      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10495      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10496      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10497      PLAYER_BITS_ANY);
10498
10499   /* ---------- execute action  -------------------------------------------- */
10500
10501   switch (action_type)
10502   {
10503     case CA_NO_ACTION:
10504     {
10505       return;
10506     }
10507
10508     /* ---------- level actions  ------------------------------------------- */
10509
10510     case CA_RESTART_LEVEL:
10511     {
10512       game.restart_level = TRUE;
10513
10514       break;
10515     }
10516
10517     case CA_SHOW_ENVELOPE:
10518     {
10519       int element = getSpecialActionElement(action_arg_element,
10520                                             action_arg_number, EL_ENVELOPE_1);
10521
10522       if (IS_ENVELOPE(element))
10523         local_player->show_envelope = element;
10524
10525       break;
10526     }
10527
10528     case CA_SET_LEVEL_TIME:
10529     {
10530       if (level.time > 0)       /* only modify limited time value */
10531       {
10532         TimeLeft = action_arg_number_new;
10533
10534 #if 1
10535         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10536
10537         DisplayGameControlValues();
10538 #else
10539         DrawGameValue_Time(TimeLeft);
10540 #endif
10541
10542         if (!TimeLeft && setup.time_limit)
10543           for (i = 0; i < MAX_PLAYERS; i++)
10544             KillPlayer(&stored_player[i]);
10545       }
10546
10547       break;
10548     }
10549
10550     case CA_SET_LEVEL_SCORE:
10551     {
10552       local_player->score = action_arg_number_new;
10553
10554 #if 1
10555       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10556
10557       DisplayGameControlValues();
10558 #else
10559       DrawGameValue_Score(local_player->score);
10560 #endif
10561
10562       break;
10563     }
10564
10565     case CA_SET_LEVEL_GEMS:
10566     {
10567       local_player->gems_still_needed = action_arg_number_new;
10568
10569 #if 1
10570       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10571
10572       DisplayGameControlValues();
10573 #else
10574       DrawGameValue_Emeralds(local_player->gems_still_needed);
10575 #endif
10576
10577       break;
10578     }
10579
10580 #if !USE_PLAYER_GRAVITY
10581     case CA_SET_LEVEL_GRAVITY:
10582     {
10583       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10584                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10585                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10586                       game.gravity);
10587       break;
10588     }
10589 #endif
10590
10591     case CA_SET_LEVEL_WIND:
10592     {
10593       game.wind_direction = action_arg_direction;
10594
10595       break;
10596     }
10597
10598     case CA_SET_LEVEL_RANDOM_SEED:
10599     {
10600 #if 1
10601       /* ensure that setting a new random seed while playing is predictable */
10602       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10603 #else
10604       InitRND(action_arg_number_new);
10605 #endif
10606
10607 #if 0
10608       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10609 #endif
10610
10611 #if 0
10612       {
10613         int i;
10614
10615         printf("::: ");
10616         for (i = 0; i < 9; i++)
10617           printf("%d, ", RND(2));
10618         printf("\n");
10619       }
10620 #endif
10621
10622       break;
10623     }
10624
10625     /* ---------- player actions  ------------------------------------------ */
10626
10627     case CA_MOVE_PLAYER:
10628     {
10629       /* automatically move to the next field in specified direction */
10630       for (i = 0; i < MAX_PLAYERS; i++)
10631         if (trigger_player_bits & (1 << i))
10632           stored_player[i].programmed_action = action_arg_direction;
10633
10634       break;
10635     }
10636
10637     case CA_EXIT_PLAYER:
10638     {
10639       for (i = 0; i < MAX_PLAYERS; i++)
10640         if (action_arg_player_bits & (1 << i))
10641           PlayerWins(&stored_player[i]);
10642
10643       break;
10644     }
10645
10646     case CA_KILL_PLAYER:
10647     {
10648       for (i = 0; i < MAX_PLAYERS; i++)
10649         if (action_arg_player_bits & (1 << i))
10650           KillPlayer(&stored_player[i]);
10651
10652       break;
10653     }
10654
10655     case CA_SET_PLAYER_KEYS:
10656     {
10657       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10658       int element = getSpecialActionElement(action_arg_element,
10659                                             action_arg_number, EL_KEY_1);
10660
10661       if (IS_KEY(element))
10662       {
10663         for (i = 0; i < MAX_PLAYERS; i++)
10664         {
10665           if (trigger_player_bits & (1 << i))
10666           {
10667             stored_player[i].key[KEY_NR(element)] = key_state;
10668
10669             DrawGameDoorValues();
10670           }
10671         }
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_PLAYER_SPEED:
10678     {
10679 #if 0
10680       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10681 #endif
10682
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684       {
10685         if (trigger_player_bits & (1 << i))
10686         {
10687           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10688
10689           if (action_arg == CA_ARG_SPEED_FASTER &&
10690               stored_player[i].cannot_move)
10691           {
10692             action_arg_number = STEPSIZE_VERY_SLOW;
10693           }
10694           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10695                    action_arg == CA_ARG_SPEED_FASTER)
10696           {
10697             action_arg_number = 2;
10698             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10699                            CA_MODE_MULTIPLY);
10700           }
10701           else if (action_arg == CA_ARG_NUMBER_RESET)
10702           {
10703             action_arg_number = level.initial_player_stepsize[i];
10704           }
10705
10706           move_stepsize =
10707             getModifiedActionNumber(move_stepsize,
10708                                     action_mode,
10709                                     action_arg_number,
10710                                     action_arg_number_min,
10711                                     action_arg_number_max);
10712
10713           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10714         }
10715       }
10716
10717       break;
10718     }
10719
10720     case CA_SET_PLAYER_SHIELD:
10721     {
10722       for (i = 0; i < MAX_PLAYERS; i++)
10723       {
10724         if (trigger_player_bits & (1 << i))
10725         {
10726           if (action_arg == CA_ARG_SHIELD_OFF)
10727           {
10728             stored_player[i].shield_normal_time_left = 0;
10729             stored_player[i].shield_deadly_time_left = 0;
10730           }
10731           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10732           {
10733             stored_player[i].shield_normal_time_left = 999999;
10734           }
10735           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10736           {
10737             stored_player[i].shield_normal_time_left = 999999;
10738             stored_player[i].shield_deadly_time_left = 999999;
10739           }
10740         }
10741       }
10742
10743       break;
10744     }
10745
10746 #if USE_PLAYER_GRAVITY
10747     case CA_SET_PLAYER_GRAVITY:
10748     {
10749       for (i = 0; i < MAX_PLAYERS; i++)
10750       {
10751         if (trigger_player_bits & (1 << i))
10752         {
10753           stored_player[i].gravity =
10754             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10755              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10756              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10757              stored_player[i].gravity);
10758         }
10759       }
10760
10761       break;
10762     }
10763 #endif
10764
10765     case CA_SET_PLAYER_ARTWORK:
10766     {
10767       for (i = 0; i < MAX_PLAYERS; i++)
10768       {
10769         if (trigger_player_bits & (1 << i))
10770         {
10771           int artwork_element = action_arg_element;
10772
10773           if (action_arg == CA_ARG_ELEMENT_RESET)
10774             artwork_element =
10775               (level.use_artwork_element[i] ? level.artwork_element[i] :
10776                stored_player[i].element_nr);
10777
10778 #if USE_GFX_RESET_PLAYER_ARTWORK
10779           if (stored_player[i].artwork_element != artwork_element)
10780             stored_player[i].Frame = 0;
10781 #endif
10782
10783           stored_player[i].artwork_element = artwork_element;
10784
10785           SetPlayerWaiting(&stored_player[i], FALSE);
10786
10787           /* set number of special actions for bored and sleeping animation */
10788           stored_player[i].num_special_action_bored =
10789             get_num_special_action(artwork_element,
10790                                    ACTION_BORING_1, ACTION_BORING_LAST);
10791           stored_player[i].num_special_action_sleeping =
10792             get_num_special_action(artwork_element,
10793                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10794         }
10795       }
10796
10797       break;
10798     }
10799
10800     case CA_SET_PLAYER_INVENTORY:
10801     {
10802       for (i = 0; i < MAX_PLAYERS; i++)
10803       {
10804         struct PlayerInfo *player = &stored_player[i];
10805         int j, k;
10806
10807         if (trigger_player_bits & (1 << i))
10808         {
10809           int inventory_element = action_arg_element;
10810
10811           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10812               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10813               action_arg == CA_ARG_ELEMENT_ACTION)
10814           {
10815             int element = inventory_element;
10816             int collect_count = element_info[element].collect_count_initial;
10817
10818             if (!IS_CUSTOM_ELEMENT(element))
10819               collect_count = 1;
10820
10821             if (collect_count == 0)
10822               player->inventory_infinite_element = element;
10823             else
10824               for (k = 0; k < collect_count; k++)
10825                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10826                   player->inventory_element[player->inventory_size++] =
10827                     element;
10828           }
10829           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10830                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10831                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10832           {
10833             if (player->inventory_infinite_element != EL_UNDEFINED &&
10834                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10835                                      action_arg_element_raw))
10836               player->inventory_infinite_element = EL_UNDEFINED;
10837
10838             for (k = 0, j = 0; j < player->inventory_size; j++)
10839             {
10840               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10841                                         action_arg_element_raw))
10842                 player->inventory_element[k++] = player->inventory_element[j];
10843             }
10844
10845             player->inventory_size = k;
10846           }
10847           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10848           {
10849             if (player->inventory_size > 0)
10850             {
10851               for (j = 0; j < player->inventory_size - 1; j++)
10852                 player->inventory_element[j] = player->inventory_element[j + 1];
10853
10854               player->inventory_size--;
10855             }
10856           }
10857           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10858           {
10859             if (player->inventory_size > 0)
10860               player->inventory_size--;
10861           }
10862           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10863           {
10864             player->inventory_infinite_element = EL_UNDEFINED;
10865             player->inventory_size = 0;
10866           }
10867           else if (action_arg == CA_ARG_INVENTORY_RESET)
10868           {
10869             player->inventory_infinite_element = EL_UNDEFINED;
10870             player->inventory_size = 0;
10871
10872             if (level.use_initial_inventory[i])
10873             {
10874               for (j = 0; j < level.initial_inventory_size[i]; j++)
10875               {
10876                 int element = level.initial_inventory_content[i][j];
10877                 int collect_count = element_info[element].collect_count_initial;
10878
10879                 if (!IS_CUSTOM_ELEMENT(element))
10880                   collect_count = 1;
10881
10882                 if (collect_count == 0)
10883                   player->inventory_infinite_element = element;
10884                 else
10885                   for (k = 0; k < collect_count; k++)
10886                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10887                       player->inventory_element[player->inventory_size++] =
10888                         element;
10889               }
10890             }
10891           }
10892         }
10893       }
10894
10895       break;
10896     }
10897
10898     /* ---------- CE actions  ---------------------------------------------- */
10899
10900     case CA_SET_CE_VALUE:
10901     {
10902 #if USE_NEW_CUSTOM_VALUE
10903       int last_ce_value = CustomValue[x][y];
10904
10905       CustomValue[x][y] = action_arg_number_new;
10906
10907       if (CustomValue[x][y] != last_ce_value)
10908       {
10909         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10910         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10911
10912         if (CustomValue[x][y] == 0)
10913         {
10914           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10915           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10916         }
10917       }
10918 #endif
10919
10920       break;
10921     }
10922
10923     case CA_SET_CE_SCORE:
10924     {
10925 #if USE_NEW_CUSTOM_VALUE
10926       int last_ce_score = ei->collect_score;
10927
10928       ei->collect_score = action_arg_number_new;
10929
10930       if (ei->collect_score != last_ce_score)
10931       {
10932         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10933         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10934
10935         if (ei->collect_score == 0)
10936         {
10937           int xx, yy;
10938
10939           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10940           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10941
10942           /*
10943             This is a very special case that seems to be a mixture between
10944             CheckElementChange() and CheckTriggeredElementChange(): while
10945             the first one only affects single elements that are triggered
10946             directly, the second one affects multiple elements in the playfield
10947             that are triggered indirectly by another element. This is a third
10948             case: Changing the CE score always affects multiple identical CEs,
10949             so every affected CE must be checked, not only the single CE for
10950             which the CE score was changed in the first place (as every instance
10951             of that CE shares the same CE score, and therefore also can change)!
10952           */
10953           SCAN_PLAYFIELD(xx, yy)
10954           {
10955             if (Feld[xx][yy] == element)
10956               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10957                                  CE_SCORE_GETS_ZERO);
10958           }
10959         }
10960       }
10961 #endif
10962
10963       break;
10964     }
10965
10966     case CA_SET_CE_ARTWORK:
10967     {
10968       int artwork_element = action_arg_element;
10969       boolean reset_frame = FALSE;
10970       int xx, yy;
10971
10972       if (action_arg == CA_ARG_ELEMENT_RESET)
10973         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10974                            element);
10975
10976       if (ei->gfx_element != artwork_element)
10977         reset_frame = TRUE;
10978
10979       ei->gfx_element = artwork_element;
10980
10981       SCAN_PLAYFIELD(xx, yy)
10982       {
10983         if (Feld[xx][yy] == element)
10984         {
10985           if (reset_frame)
10986           {
10987             ResetGfxAnimation(xx, yy);
10988             ResetRandomAnimationValue(xx, yy);
10989           }
10990
10991           TEST_DrawLevelField(xx, yy);
10992         }
10993       }
10994
10995       break;
10996     }
10997
10998     /* ---------- engine actions  ------------------------------------------ */
10999
11000     case CA_SET_ENGINE_SCAN_MODE:
11001     {
11002       InitPlayfieldScanMode(action_arg);
11003
11004       break;
11005     }
11006
11007     default:
11008       break;
11009   }
11010 }
11011
11012 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11013 {
11014   int old_element = Feld[x][y];
11015   int new_element = GetElementFromGroupElement(element);
11016   int previous_move_direction = MovDir[x][y];
11017 #if USE_NEW_CUSTOM_VALUE
11018   int last_ce_value = CustomValue[x][y];
11019 #endif
11020   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11021   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11022   boolean add_player_onto_element = (new_element_is_player &&
11023 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11024                                      /* this breaks SnakeBite when a snake is
11025                                         halfway through a door that closes */
11026                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11027                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11028 #endif
11029                                      IS_WALKABLE(old_element));
11030
11031 #if 0
11032   /* check if element under the player changes from accessible to unaccessible
11033      (needed for special case of dropping element which then changes) */
11034   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11035       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11036   {
11037     Bang(x, y);
11038
11039     return;
11040   }
11041 #endif
11042
11043   if (!add_player_onto_element)
11044   {
11045     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11046       RemoveMovingField(x, y);
11047     else
11048       RemoveField(x, y);
11049
11050     Feld[x][y] = new_element;
11051
11052 #if !USE_GFX_RESET_GFX_ANIMATION
11053     ResetGfxAnimation(x, y);
11054     ResetRandomAnimationValue(x, y);
11055 #endif
11056
11057     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11058       MovDir[x][y] = previous_move_direction;
11059
11060 #if USE_NEW_CUSTOM_VALUE
11061     if (element_info[new_element].use_last_ce_value)
11062       CustomValue[x][y] = last_ce_value;
11063 #endif
11064
11065     InitField_WithBug1(x, y, FALSE);
11066
11067     new_element = Feld[x][y];   /* element may have changed */
11068
11069 #if USE_GFX_RESET_GFX_ANIMATION
11070     ResetGfxAnimation(x, y);
11071     ResetRandomAnimationValue(x, y);
11072 #endif
11073
11074     TEST_DrawLevelField(x, y);
11075
11076     if (GFX_CRUMBLED(new_element))
11077       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11078   }
11079
11080 #if 1
11081   /* check if element under the player changes from accessible to unaccessible
11082      (needed for special case of dropping element which then changes) */
11083   /* (must be checked after creating new element for walkable group elements) */
11084 #if USE_FIX_KILLED_BY_NON_WALKABLE
11085   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11086       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11087   {
11088     Bang(x, y);
11089
11090     return;
11091   }
11092 #else
11093   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11094       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11095   {
11096     Bang(x, y);
11097
11098     return;
11099   }
11100 #endif
11101 #endif
11102
11103   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11104   if (new_element_is_player)
11105     RelocatePlayer(x, y, new_element);
11106
11107   if (is_change)
11108     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11109
11110   TestIfBadThingTouchesPlayer(x, y);
11111   TestIfPlayerTouchesCustomElement(x, y);
11112   TestIfElementTouchesCustomElement(x, y);
11113 }
11114
11115 static void CreateField(int x, int y, int element)
11116 {
11117   CreateFieldExt(x, y, element, FALSE);
11118 }
11119
11120 static void CreateElementFromChange(int x, int y, int element)
11121 {
11122   element = GET_VALID_RUNTIME_ELEMENT(element);
11123
11124 #if USE_STOP_CHANGED_ELEMENTS
11125   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11126   {
11127     int old_element = Feld[x][y];
11128
11129     /* prevent changed element from moving in same engine frame
11130        unless both old and new element can either fall or move */
11131     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11132         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11133       Stop[x][y] = TRUE;
11134   }
11135 #endif
11136
11137   CreateFieldExt(x, y, element, TRUE);
11138 }
11139
11140 static boolean ChangeElement(int x, int y, int element, int page)
11141 {
11142   struct ElementInfo *ei = &element_info[element];
11143   struct ElementChangeInfo *change = &ei->change_page[page];
11144   int ce_value = CustomValue[x][y];
11145   int ce_score = ei->collect_score;
11146   int target_element;
11147   int old_element = Feld[x][y];
11148
11149   /* always use default change event to prevent running into a loop */
11150   if (ChangeEvent[x][y] == -1)
11151     ChangeEvent[x][y] = CE_DELAY;
11152
11153   if (ChangeEvent[x][y] == CE_DELAY)
11154   {
11155     /* reset actual trigger element, trigger player and action element */
11156     change->actual_trigger_element = EL_EMPTY;
11157     change->actual_trigger_player = EL_EMPTY;
11158     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11159     change->actual_trigger_side = CH_SIDE_NONE;
11160     change->actual_trigger_ce_value = 0;
11161     change->actual_trigger_ce_score = 0;
11162   }
11163
11164   /* do not change elements more than a specified maximum number of changes */
11165   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11166     return FALSE;
11167
11168   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11169
11170   if (change->explode)
11171   {
11172     Bang(x, y);
11173
11174     return TRUE;
11175   }
11176
11177   if (change->use_target_content)
11178   {
11179     boolean complete_replace = TRUE;
11180     boolean can_replace[3][3];
11181     int xx, yy;
11182
11183     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11184     {
11185       boolean is_empty;
11186       boolean is_walkable;
11187       boolean is_diggable;
11188       boolean is_collectible;
11189       boolean is_removable;
11190       boolean is_destructible;
11191       int ex = x + xx - 1;
11192       int ey = y + yy - 1;
11193       int content_element = change->target_content.e[xx][yy];
11194       int e;
11195
11196       can_replace[xx][yy] = TRUE;
11197
11198       if (ex == x && ey == y)   /* do not check changing element itself */
11199         continue;
11200
11201       if (content_element == EL_EMPTY_SPACE)
11202       {
11203         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11204
11205         continue;
11206       }
11207
11208       if (!IN_LEV_FIELD(ex, ey))
11209       {
11210         can_replace[xx][yy] = FALSE;
11211         complete_replace = FALSE;
11212
11213         continue;
11214       }
11215
11216       e = Feld[ex][ey];
11217
11218       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11219         e = MovingOrBlocked2Element(ex, ey);
11220
11221       is_empty = (IS_FREE(ex, ey) ||
11222                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11223
11224       is_walkable     = (is_empty || IS_WALKABLE(e));
11225       is_diggable     = (is_empty || IS_DIGGABLE(e));
11226       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11227       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11228       is_removable    = (is_diggable || is_collectible);
11229
11230       can_replace[xx][yy] =
11231         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11232           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11233           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11234           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11235           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11236           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11237          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11238
11239       if (!can_replace[xx][yy])
11240         complete_replace = FALSE;
11241     }
11242
11243     if (!change->only_if_complete || complete_replace)
11244     {
11245       boolean something_has_changed = FALSE;
11246
11247       if (change->only_if_complete && change->use_random_replace &&
11248           RND(100) < change->random_percentage)
11249         return FALSE;
11250
11251       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11252       {
11253         int ex = x + xx - 1;
11254         int ey = y + yy - 1;
11255         int content_element;
11256
11257         if (can_replace[xx][yy] && (!change->use_random_replace ||
11258                                     RND(100) < change->random_percentage))
11259         {
11260           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11261             RemoveMovingField(ex, ey);
11262
11263           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11264
11265           content_element = change->target_content.e[xx][yy];
11266           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11267                                               ce_value, ce_score);
11268
11269           CreateElementFromChange(ex, ey, target_element);
11270
11271           something_has_changed = TRUE;
11272
11273           /* for symmetry reasons, freeze newly created border elements */
11274           if (ex != x || ey != y)
11275             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11276         }
11277       }
11278
11279       if (something_has_changed)
11280       {
11281         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11282         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11283       }
11284     }
11285   }
11286   else
11287   {
11288     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11289                                         ce_value, ce_score);
11290
11291     if (element == EL_DIAGONAL_GROWING ||
11292         element == EL_DIAGONAL_SHRINKING)
11293     {
11294       target_element = Store[x][y];
11295
11296       Store[x][y] = EL_EMPTY;
11297     }
11298
11299     CreateElementFromChange(x, y, target_element);
11300
11301     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11302     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11303   }
11304
11305   /* this uses direct change before indirect change */
11306   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11307
11308   return TRUE;
11309 }
11310
11311 #if USE_NEW_DELAYED_ACTION
11312
11313 static void HandleElementChange(int x, int y, int page)
11314 {
11315   int element = MovingOrBlocked2Element(x, y);
11316   struct ElementInfo *ei = &element_info[element];
11317   struct ElementChangeInfo *change = &ei->change_page[page];
11318   boolean handle_action_before_change = FALSE;
11319
11320 #ifdef DEBUG
11321   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11322       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11323   {
11324     printf("\n\n");
11325     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11326            x, y, element, element_info[element].token_name);
11327     printf("HandleElementChange(): This should never happen!\n");
11328     printf("\n\n");
11329   }
11330 #endif
11331
11332   /* this can happen with classic bombs on walkable, changing elements */
11333   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11334   {
11335 #if 0
11336     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11337       ChangeDelay[x][y] = 0;
11338 #endif
11339
11340     return;
11341   }
11342
11343   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11344   {
11345     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11346
11347     if (change->can_change)
11348     {
11349 #if 1
11350       /* !!! not clear why graphic animation should be reset at all here !!! */
11351       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11352 #if USE_GFX_RESET_WHEN_NOT_MOVING
11353       /* when a custom element is about to change (for example by change delay),
11354          do not reset graphic animation when the custom element is moving */
11355       if (!IS_MOVING(x, y))
11356 #endif
11357       {
11358         ResetGfxAnimation(x, y);
11359         ResetRandomAnimationValue(x, y);
11360       }
11361 #endif
11362
11363       if (change->pre_change_function)
11364         change->pre_change_function(x, y);
11365     }
11366   }
11367
11368   ChangeDelay[x][y]--;
11369
11370   if (ChangeDelay[x][y] != 0)           /* continue element change */
11371   {
11372     if (change->can_change)
11373     {
11374       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11375
11376       if (IS_ANIMATED(graphic))
11377         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11378
11379       if (change->change_function)
11380         change->change_function(x, y);
11381     }
11382   }
11383   else                                  /* finish element change */
11384   {
11385     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11386     {
11387       page = ChangePage[x][y];
11388       ChangePage[x][y] = -1;
11389
11390       change = &ei->change_page[page];
11391     }
11392
11393     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11394     {
11395       ChangeDelay[x][y] = 1;            /* try change after next move step */
11396       ChangePage[x][y] = page;          /* remember page to use for change */
11397
11398       return;
11399     }
11400
11401 #if 1
11402     /* special case: set new level random seed before changing element */
11403     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11404       handle_action_before_change = TRUE;
11405
11406     if (change->has_action && handle_action_before_change)
11407       ExecuteCustomElementAction(x, y, element, page);
11408 #endif
11409
11410     if (change->can_change)
11411     {
11412       if (ChangeElement(x, y, element, page))
11413       {
11414         if (change->post_change_function)
11415           change->post_change_function(x, y);
11416       }
11417     }
11418
11419     if (change->has_action && !handle_action_before_change)
11420       ExecuteCustomElementAction(x, y, element, page);
11421   }
11422 }
11423
11424 #else
11425
11426 static void HandleElementChange(int x, int y, int page)
11427 {
11428   int element = MovingOrBlocked2Element(x, y);
11429   struct ElementInfo *ei = &element_info[element];
11430   struct ElementChangeInfo *change = &ei->change_page[page];
11431
11432 #ifdef DEBUG
11433   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11434   {
11435     printf("\n\n");
11436     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11437            x, y, element, element_info[element].token_name);
11438     printf("HandleElementChange(): This should never happen!\n");
11439     printf("\n\n");
11440   }
11441 #endif
11442
11443   /* this can happen with classic bombs on walkable, changing elements */
11444   if (!CAN_CHANGE(element))
11445   {
11446 #if 0
11447     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11448       ChangeDelay[x][y] = 0;
11449 #endif
11450
11451     return;
11452   }
11453
11454   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11455   {
11456     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11457
11458     ResetGfxAnimation(x, y);
11459     ResetRandomAnimationValue(x, y);
11460
11461     if (change->pre_change_function)
11462       change->pre_change_function(x, y);
11463   }
11464
11465   ChangeDelay[x][y]--;
11466
11467   if (ChangeDelay[x][y] != 0)           /* continue element change */
11468   {
11469     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11470
11471     if (IS_ANIMATED(graphic))
11472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11473
11474     if (change->change_function)
11475       change->change_function(x, y);
11476   }
11477   else                                  /* finish element change */
11478   {
11479     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11480     {
11481       page = ChangePage[x][y];
11482       ChangePage[x][y] = -1;
11483
11484       change = &ei->change_page[page];
11485     }
11486
11487     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11488     {
11489       ChangeDelay[x][y] = 1;            /* try change after next move step */
11490       ChangePage[x][y] = page;          /* remember page to use for change */
11491
11492       return;
11493     }
11494
11495     if (ChangeElement(x, y, element, page))
11496     {
11497       if (change->post_change_function)
11498         change->post_change_function(x, y);
11499     }
11500   }
11501 }
11502
11503 #endif
11504
11505 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11506                                               int trigger_element,
11507                                               int trigger_event,
11508                                               int trigger_player,
11509                                               int trigger_side,
11510                                               int trigger_page)
11511 {
11512   boolean change_done_any = FALSE;
11513   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11514   int i;
11515
11516   if (!(trigger_events[trigger_element][trigger_event]))
11517     return FALSE;
11518
11519 #if 0
11520   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11521          trigger_event, recursion_loop_depth, recursion_loop_detected,
11522          recursion_loop_element, EL_NAME(recursion_loop_element));
11523 #endif
11524
11525   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11526
11527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11528   {
11529     int element = EL_CUSTOM_START + i;
11530     boolean change_done = FALSE;
11531     int p;
11532
11533     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11534         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11535       continue;
11536
11537     for (p = 0; p < element_info[element].num_change_pages; p++)
11538     {
11539       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11540
11541       if (change->can_change_or_has_action &&
11542           change->has_event[trigger_event] &&
11543           change->trigger_side & trigger_side &&
11544           change->trigger_player & trigger_player &&
11545           change->trigger_page & trigger_page_bits &&
11546           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11547       {
11548         change->actual_trigger_element = trigger_element;
11549         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11550         change->actual_trigger_player_bits = trigger_player;
11551         change->actual_trigger_side = trigger_side;
11552         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11553         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11554
11555 #if 0
11556         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11557                element, EL_NAME(element), p);
11558 #endif
11559
11560         if ((change->can_change && !change_done) || change->has_action)
11561         {
11562           int x, y;
11563
11564           SCAN_PLAYFIELD(x, y)
11565           {
11566             if (Feld[x][y] == element)
11567             {
11568               if (change->can_change && !change_done)
11569               {
11570 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11571                 /* if element already changed in this frame, not only prevent
11572                    another element change (checked in ChangeElement()), but
11573                    also prevent additional element actions for this element */
11574
11575                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11576                     !level.use_action_after_change_bug)
11577                   continue;
11578 #endif
11579
11580 #if 0
11581                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11582                        element, EL_NAME(element), p);
11583 #endif
11584
11585                 ChangeDelay[x][y] = 1;
11586                 ChangeEvent[x][y] = trigger_event;
11587
11588                 HandleElementChange(x, y, p);
11589               }
11590 #if USE_NEW_DELAYED_ACTION
11591               else if (change->has_action)
11592               {
11593 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11594                 /* if element already changed in this frame, not only prevent
11595                    another element change (checked in ChangeElement()), but
11596                    also prevent additional element actions for this element */
11597
11598                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11599                     !level.use_action_after_change_bug)
11600                   continue;
11601 #endif
11602
11603
11604 #if 0
11605                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11606                        element, EL_NAME(element), p);
11607 #endif
11608
11609                 ExecuteCustomElementAction(x, y, element, p);
11610                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11611               }
11612 #else
11613               if (change->has_action)
11614               {
11615                 ExecuteCustomElementAction(x, y, element, p);
11616                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11617               }
11618 #endif
11619             }
11620           }
11621
11622           if (change->can_change)
11623           {
11624             change_done = TRUE;
11625             change_done_any = TRUE;
11626
11627 #if 0
11628             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11629                    element, EL_NAME(element), p);
11630 #endif
11631
11632           }
11633         }
11634       }
11635     }
11636   }
11637
11638   RECURSION_LOOP_DETECTION_END();
11639
11640   return change_done_any;
11641 }
11642
11643 static boolean CheckElementChangeExt(int x, int y,
11644                                      int element,
11645                                      int trigger_element,
11646                                      int trigger_event,
11647                                      int trigger_player,
11648                                      int trigger_side)
11649 {
11650   boolean change_done = FALSE;
11651   int p;
11652
11653   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11654       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11655     return FALSE;
11656
11657   if (Feld[x][y] == EL_BLOCKED)
11658   {
11659     Blocked2Moving(x, y, &x, &y);
11660     element = Feld[x][y];
11661   }
11662
11663 #if 0
11664   /* check if element has already changed */
11665   if (Feld[x][y] != element)
11666     return FALSE;
11667 #else
11668   /* check if element has already changed or is about to change after moving */
11669   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11670        Feld[x][y] != element) ||
11671
11672       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11673        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11674         ChangePage[x][y] != -1)))
11675     return FALSE;
11676 #endif
11677
11678 #if 0
11679   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11680          trigger_event, recursion_loop_depth, recursion_loop_detected,
11681          recursion_loop_element, EL_NAME(recursion_loop_element));
11682 #endif
11683
11684   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11685
11686 #if 0
11687   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11688 #endif
11689
11690   for (p = 0; p < element_info[element].num_change_pages; p++)
11691   {
11692     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11693
11694     /* check trigger element for all events where the element that is checked
11695        for changing interacts with a directly adjacent element -- this is
11696        different to element changes that affect other elements to change on the
11697        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11698     boolean check_trigger_element =
11699       (trigger_event == CE_TOUCHING_X ||
11700        trigger_event == CE_HITTING_X ||
11701        trigger_event == CE_HIT_BY_X ||
11702 #if 1
11703        /* this one was forgotten until 3.2.3 */
11704        trigger_event == CE_DIGGING_X);
11705 #endif
11706
11707     if (change->can_change_or_has_action &&
11708         change->has_event[trigger_event] &&
11709         change->trigger_side & trigger_side &&
11710         change->trigger_player & trigger_player &&
11711         (!check_trigger_element ||
11712          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11713     {
11714       change->actual_trigger_element = trigger_element;
11715       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11716       change->actual_trigger_player_bits = trigger_player;
11717       change->actual_trigger_side = trigger_side;
11718       change->actual_trigger_ce_value = CustomValue[x][y];
11719       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11720
11721       /* special case: trigger element not at (x,y) position for some events */
11722       if (check_trigger_element)
11723       {
11724         static struct
11725         {
11726           int dx, dy;
11727         } move_xy[] =
11728           {
11729             {  0,  0 },
11730             { -1,  0 },
11731             { +1,  0 },
11732             {  0,  0 },
11733             {  0, -1 },
11734             {  0,  0 }, { 0, 0 }, { 0, 0 },
11735             {  0, +1 }
11736           };
11737
11738         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11739         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11740
11741         change->actual_trigger_ce_value = CustomValue[xx][yy];
11742         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11743       }
11744
11745       if (change->can_change && !change_done)
11746       {
11747         ChangeDelay[x][y] = 1;
11748         ChangeEvent[x][y] = trigger_event;
11749
11750         HandleElementChange(x, y, p);
11751
11752         change_done = TRUE;
11753       }
11754 #if USE_NEW_DELAYED_ACTION
11755       else if (change->has_action)
11756       {
11757         ExecuteCustomElementAction(x, y, element, p);
11758         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11759       }
11760 #else
11761       if (change->has_action)
11762       {
11763         ExecuteCustomElementAction(x, y, element, p);
11764         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11765       }
11766 #endif
11767     }
11768   }
11769
11770   RECURSION_LOOP_DETECTION_END();
11771
11772   return change_done;
11773 }
11774
11775 static void PlayPlayerSound(struct PlayerInfo *player)
11776 {
11777   int jx = player->jx, jy = player->jy;
11778   int sound_element = player->artwork_element;
11779   int last_action = player->last_action_waiting;
11780   int action = player->action_waiting;
11781
11782   if (player->is_waiting)
11783   {
11784     if (action != last_action)
11785       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11786     else
11787       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11788   }
11789   else
11790   {
11791     if (action != last_action)
11792       StopSound(element_info[sound_element].sound[last_action]);
11793
11794     if (last_action == ACTION_SLEEPING)
11795       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11796   }
11797 }
11798
11799 static void PlayAllPlayersSound()
11800 {
11801   int i;
11802
11803   for (i = 0; i < MAX_PLAYERS; i++)
11804     if (stored_player[i].active)
11805       PlayPlayerSound(&stored_player[i]);
11806 }
11807
11808 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11809 {
11810   boolean last_waiting = player->is_waiting;
11811   int move_dir = player->MovDir;
11812
11813   player->dir_waiting = move_dir;
11814   player->last_action_waiting = player->action_waiting;
11815
11816   if (is_waiting)
11817   {
11818     if (!last_waiting)          /* not waiting -> waiting */
11819     {
11820       player->is_waiting = TRUE;
11821
11822       player->frame_counter_bored =
11823         FrameCounter +
11824         game.player_boring_delay_fixed +
11825         GetSimpleRandom(game.player_boring_delay_random);
11826       player->frame_counter_sleeping =
11827         FrameCounter +
11828         game.player_sleeping_delay_fixed +
11829         GetSimpleRandom(game.player_sleeping_delay_random);
11830
11831       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11832     }
11833
11834     if (game.player_sleeping_delay_fixed +
11835         game.player_sleeping_delay_random > 0 &&
11836         player->anim_delay_counter == 0 &&
11837         player->post_delay_counter == 0 &&
11838         FrameCounter >= player->frame_counter_sleeping)
11839       player->is_sleeping = TRUE;
11840     else if (game.player_boring_delay_fixed +
11841              game.player_boring_delay_random > 0 &&
11842              FrameCounter >= player->frame_counter_bored)
11843       player->is_bored = TRUE;
11844
11845     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11846                               player->is_bored ? ACTION_BORING :
11847                               ACTION_WAITING);
11848
11849     if (player->is_sleeping && player->use_murphy)
11850     {
11851       /* special case for sleeping Murphy when leaning against non-free tile */
11852
11853       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11854           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11855            !IS_MOVING(player->jx - 1, player->jy)))
11856         move_dir = MV_LEFT;
11857       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11858                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11859                 !IS_MOVING(player->jx + 1, player->jy)))
11860         move_dir = MV_RIGHT;
11861       else
11862         player->is_sleeping = FALSE;
11863
11864       player->dir_waiting = move_dir;
11865     }
11866
11867     if (player->is_sleeping)
11868     {
11869       if (player->num_special_action_sleeping > 0)
11870       {
11871         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11872         {
11873           int last_special_action = player->special_action_sleeping;
11874           int num_special_action = player->num_special_action_sleeping;
11875           int special_action =
11876             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11877              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11878              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11879              last_special_action + 1 : ACTION_SLEEPING);
11880           int special_graphic =
11881             el_act_dir2img(player->artwork_element, special_action, move_dir);
11882
11883           player->anim_delay_counter =
11884             graphic_info[special_graphic].anim_delay_fixed +
11885             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11886           player->post_delay_counter =
11887             graphic_info[special_graphic].post_delay_fixed +
11888             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11889
11890           player->special_action_sleeping = special_action;
11891         }
11892
11893         if (player->anim_delay_counter > 0)
11894         {
11895           player->action_waiting = player->special_action_sleeping;
11896           player->anim_delay_counter--;
11897         }
11898         else if (player->post_delay_counter > 0)
11899         {
11900           player->post_delay_counter--;
11901         }
11902       }
11903     }
11904     else if (player->is_bored)
11905     {
11906       if (player->num_special_action_bored > 0)
11907       {
11908         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11909         {
11910           int special_action =
11911             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11912           int special_graphic =
11913             el_act_dir2img(player->artwork_element, special_action, move_dir);
11914
11915           player->anim_delay_counter =
11916             graphic_info[special_graphic].anim_delay_fixed +
11917             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11918           player->post_delay_counter =
11919             graphic_info[special_graphic].post_delay_fixed +
11920             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11921
11922           player->special_action_bored = special_action;
11923         }
11924
11925         if (player->anim_delay_counter > 0)
11926         {
11927           player->action_waiting = player->special_action_bored;
11928           player->anim_delay_counter--;
11929         }
11930         else if (player->post_delay_counter > 0)
11931         {
11932           player->post_delay_counter--;
11933         }
11934       }
11935     }
11936   }
11937   else if (last_waiting)        /* waiting -> not waiting */
11938   {
11939     player->is_waiting = FALSE;
11940     player->is_bored = FALSE;
11941     player->is_sleeping = FALSE;
11942
11943     player->frame_counter_bored = -1;
11944     player->frame_counter_sleeping = -1;
11945
11946     player->anim_delay_counter = 0;
11947     player->post_delay_counter = 0;
11948
11949     player->dir_waiting = player->MovDir;
11950     player->action_waiting = ACTION_DEFAULT;
11951
11952     player->special_action_bored = ACTION_DEFAULT;
11953     player->special_action_sleeping = ACTION_DEFAULT;
11954   }
11955 }
11956
11957 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11958 {
11959   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11960   int left      = player_action & JOY_LEFT;
11961   int right     = player_action & JOY_RIGHT;
11962   int up        = player_action & JOY_UP;
11963   int down      = player_action & JOY_DOWN;
11964   int button1   = player_action & JOY_BUTTON_1;
11965   int button2   = player_action & JOY_BUTTON_2;
11966   int dx        = (left ? -1 : right ? 1 : 0);
11967   int dy        = (up   ? -1 : down  ? 1 : 0);
11968
11969   if (!player->active || tape.pausing)
11970     return 0;
11971
11972   if (player_action)
11973   {
11974     if (button1)
11975       snapped = SnapField(player, dx, dy);
11976     else
11977     {
11978       if (button2)
11979         dropped = DropElement(player);
11980
11981       moved = MovePlayer(player, dx, dy);
11982     }
11983
11984     if (tape.single_step && tape.recording && !tape.pausing)
11985     {
11986       if (button1 || (dropped && !moved))
11987       {
11988         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11989         SnapField(player, 0, 0);                /* stop snapping */
11990       }
11991     }
11992
11993     SetPlayerWaiting(player, FALSE);
11994
11995     return player_action;
11996   }
11997   else
11998   {
11999     /* no actions for this player (no input at player's configured device) */
12000
12001     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12002     SnapField(player, 0, 0);
12003     CheckGravityMovementWhenNotMoving(player);
12004
12005     if (player->MovPos == 0)
12006       SetPlayerWaiting(player, TRUE);
12007
12008     if (player->MovPos == 0)    /* needed for tape.playing */
12009       player->is_moving = FALSE;
12010
12011     player->is_dropping = FALSE;
12012     player->is_dropping_pressed = FALSE;
12013     player->drop_pressed_delay = 0;
12014
12015     return 0;
12016   }
12017 }
12018
12019 static void CheckLevelTime()
12020 {
12021   int i;
12022
12023   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12024   {
12025     if (level.native_em_level->lev->home == 0)  /* all players at home */
12026     {
12027       PlayerWins(local_player);
12028
12029       AllPlayersGone = TRUE;
12030
12031       level.native_em_level->lev->home = -1;
12032     }
12033
12034     if (level.native_em_level->ply[0]->alive == 0 &&
12035         level.native_em_level->ply[1]->alive == 0 &&
12036         level.native_em_level->ply[2]->alive == 0 &&
12037         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12038       AllPlayersGone = TRUE;
12039   }
12040   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12041   {
12042     if (game_sp_info.LevelSolved)                       /* game won */
12043     {
12044       PlayerWins(local_player);
12045
12046       AllPlayersGone = TRUE;
12047     }
12048
12049     if (game_sp_info.GameOver)                          /* game lost */
12050       AllPlayersGone = TRUE;
12051   }
12052
12053   if (TimeFrames >= FRAMES_PER_SECOND)
12054   {
12055     TimeFrames = 0;
12056     TapeTime++;
12057
12058     for (i = 0; i < MAX_PLAYERS; i++)
12059     {
12060       struct PlayerInfo *player = &stored_player[i];
12061
12062       if (SHIELD_ON(player))
12063       {
12064         player->shield_normal_time_left--;
12065
12066         if (player->shield_deadly_time_left > 0)
12067           player->shield_deadly_time_left--;
12068       }
12069     }
12070
12071     if (!local_player->LevelSolved && !level.use_step_counter)
12072     {
12073       TimePlayed++;
12074
12075       if (TimeLeft > 0)
12076       {
12077         TimeLeft--;
12078
12079         if (TimeLeft <= 10 && setup.time_limit)
12080           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12081
12082 #if 1
12083         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12084
12085         DisplayGameControlValues();
12086 #else
12087         DrawGameValue_Time(TimeLeft);
12088 #endif
12089
12090         if (!TimeLeft && setup.time_limit)
12091         {
12092           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12093             level.native_em_level->lev->killed_out_of_time = TRUE;
12094           else
12095             for (i = 0; i < MAX_PLAYERS; i++)
12096               KillPlayer(&stored_player[i]);
12097         }
12098       }
12099 #if 1
12100       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12101       {
12102         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12103
12104         DisplayGameControlValues();
12105       }
12106 #else
12107       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12108         DrawGameValue_Time(TimePlayed);
12109 #endif
12110
12111       level.native_em_level->lev->time =
12112         (level.time == 0 ? TimePlayed : TimeLeft);
12113     }
12114
12115     if (tape.recording || tape.playing)
12116       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12117   }
12118
12119 #if 1
12120   UpdateAndDisplayGameControlValues();
12121 #else
12122   UpdateGameDoorValues();
12123   DrawGameDoorValues();
12124 #endif
12125 }
12126
12127 void AdvanceFrameAndPlayerCounters(int player_nr)
12128 {
12129   int i;
12130
12131   /* advance frame counters (global frame counter and time frame counter) */
12132   FrameCounter++;
12133   TimeFrames++;
12134
12135   /* advance player counters (counters for move delay, move animation etc.) */
12136   for (i = 0; i < MAX_PLAYERS; i++)
12137   {
12138     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12139     int move_delay_value = stored_player[i].move_delay_value;
12140     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12141
12142     if (!advance_player_counters)       /* not all players may be affected */
12143       continue;
12144
12145 #if USE_NEW_PLAYER_ANIM
12146     if (move_frames == 0)       /* less than one move per game frame */
12147     {
12148       int stepsize = TILEX / move_delay_value;
12149       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12150       int count = (stored_player[i].is_moving ?
12151                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12152
12153       if (count % delay == 0)
12154         move_frames = 1;
12155     }
12156 #endif
12157
12158     stored_player[i].Frame += move_frames;
12159
12160     if (stored_player[i].MovPos != 0)
12161       stored_player[i].StepFrame += move_frames;
12162
12163     if (stored_player[i].move_delay > 0)
12164       stored_player[i].move_delay--;
12165
12166     /* due to bugs in previous versions, counter must count up, not down */
12167     if (stored_player[i].push_delay != -1)
12168       stored_player[i].push_delay++;
12169
12170     if (stored_player[i].drop_delay > 0)
12171       stored_player[i].drop_delay--;
12172
12173     if (stored_player[i].is_dropping_pressed)
12174       stored_player[i].drop_pressed_delay++;
12175   }
12176 }
12177
12178 void StartGameActions(boolean init_network_game, boolean record_tape,
12179                       long random_seed)
12180 {
12181   unsigned long new_random_seed = InitRND(random_seed);
12182
12183   if (record_tape)
12184     TapeStartRecording(new_random_seed);
12185
12186 #if defined(NETWORK_AVALIABLE)
12187   if (init_network_game)
12188   {
12189     SendToServer_StartPlaying();
12190
12191     return;
12192   }
12193 #endif
12194
12195   InitGame();
12196 }
12197
12198 void GameActions()
12199 {
12200   static unsigned long game_frame_delay = 0;
12201   unsigned long game_frame_delay_value;
12202   byte *recorded_player_action;
12203   byte summarized_player_action = 0;
12204   byte tape_action[MAX_PLAYERS];
12205   int i;
12206
12207   /* detect endless loops, caused by custom element programming */
12208   if (recursion_loop_detected && recursion_loop_depth == 0)
12209   {
12210     char *message = getStringCat3("Internal Error ! Element ",
12211                                   EL_NAME(recursion_loop_element),
12212                                   " caused endless loop ! Quit the game ?");
12213
12214     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12215           EL_NAME(recursion_loop_element));
12216
12217     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12218
12219     recursion_loop_detected = FALSE;    /* if game should be continued */
12220
12221     free(message);
12222
12223     return;
12224   }
12225
12226   if (game.restart_level)
12227     StartGameActions(options.network, setup.autorecord, level.random_seed);
12228
12229   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12230   {
12231     if (level.native_em_level->lev->home == 0)  /* all players at home */
12232     {
12233       PlayerWins(local_player);
12234
12235       AllPlayersGone = TRUE;
12236
12237       level.native_em_level->lev->home = -1;
12238     }
12239
12240     if (level.native_em_level->ply[0]->alive == 0 &&
12241         level.native_em_level->ply[1]->alive == 0 &&
12242         level.native_em_level->ply[2]->alive == 0 &&
12243         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12244       AllPlayersGone = TRUE;
12245   }
12246
12247   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12248     GameWon();
12249
12250   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12251     TapeStop();
12252
12253   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12254     return;
12255
12256   game_frame_delay_value =
12257     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12258
12259   if (tape.playing && tape.warp_forward && !tape.pausing)
12260     game_frame_delay_value = 0;
12261
12262   /* ---------- main game synchronization point ---------- */
12263
12264   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12265
12266   if (network_playing && !network_player_action_received)
12267   {
12268     /* try to get network player actions in time */
12269
12270 #if defined(NETWORK_AVALIABLE)
12271     /* last chance to get network player actions without main loop delay */
12272     HandleNetworking();
12273 #endif
12274
12275     /* game was quit by network peer */
12276     if (game_status != GAME_MODE_PLAYING)
12277       return;
12278
12279     if (!network_player_action_received)
12280       return;           /* failed to get network player actions in time */
12281
12282     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12283   }
12284
12285   if (tape.pausing)
12286     return;
12287
12288   /* at this point we know that we really continue executing the game */
12289
12290   network_player_action_received = FALSE;
12291
12292   /* when playing tape, read previously recorded player input from tape data */
12293   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12294
12295 #if 1
12296   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12297   if (tape.pausing)
12298     return;
12299 #endif
12300
12301   if (tape.set_centered_player)
12302   {
12303     game.centered_player_nr_next = tape.centered_player_nr_next;
12304     game.set_centered_player = TRUE;
12305   }
12306
12307   for (i = 0; i < MAX_PLAYERS; i++)
12308   {
12309     summarized_player_action |= stored_player[i].action;
12310
12311     if (!network_playing)
12312       stored_player[i].effective_action = stored_player[i].action;
12313   }
12314
12315 #if defined(NETWORK_AVALIABLE)
12316   if (network_playing)
12317     SendToServer_MovePlayer(summarized_player_action);
12318 #endif
12319
12320   if (!options.network && !setup.team_mode)
12321     local_player->effective_action = summarized_player_action;
12322
12323   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12324   {
12325     for (i = 0; i < MAX_PLAYERS; i++)
12326       stored_player[i].effective_action =
12327         (i == game.centered_player_nr ? summarized_player_action : 0);
12328   }
12329
12330   if (recorded_player_action != NULL)
12331     for (i = 0; i < MAX_PLAYERS; i++)
12332       stored_player[i].effective_action = recorded_player_action[i];
12333
12334   for (i = 0; i < MAX_PLAYERS; i++)
12335   {
12336     tape_action[i] = stored_player[i].effective_action;
12337
12338     /* (this can only happen in the R'n'D game engine) */
12339     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12340       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12341   }
12342
12343   /* only record actions from input devices, but not programmed actions */
12344   if (tape.recording)
12345     TapeRecordAction(tape_action);
12346
12347 #if USE_NEW_PLAYER_ASSIGNMENTS
12348   {
12349     byte mapped_action[MAX_PLAYERS];
12350
12351     for (i = 0; i < MAX_PLAYERS; i++)
12352       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12353
12354     for (i = 0; i < MAX_PLAYERS; i++)
12355       stored_player[i].effective_action = mapped_action[i];
12356   }
12357 #endif
12358
12359   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12360   {
12361     GameActions_EM_Main();
12362   }
12363   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12364   {
12365     GameActions_SP_Main();
12366   }
12367   else
12368   {
12369     GameActions_RND();
12370   }
12371 }
12372
12373 void GameActions_EM_Main()
12374 {
12375   byte effective_action[MAX_PLAYERS];
12376   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12377   int i;
12378
12379   for (i = 0; i < MAX_PLAYERS; i++)
12380     effective_action[i] = stored_player[i].effective_action;
12381
12382   GameActions_EM(effective_action, warp_mode);
12383
12384   CheckLevelTime();
12385
12386   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12387 }
12388
12389 void GameActions_SP_Main()
12390 {
12391   byte effective_action[MAX_PLAYERS];
12392   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12393   int i;
12394
12395   for (i = 0; i < MAX_PLAYERS; i++)
12396     effective_action[i] = stored_player[i].effective_action;
12397
12398   GameActions_SP(effective_action, warp_mode);
12399
12400   CheckLevelTime();
12401
12402   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12403 }
12404
12405 void GameActions_RND()
12406 {
12407   int magic_wall_x = 0, magic_wall_y = 0;
12408   int i, x, y, element, graphic;
12409
12410   InitPlayfieldScanModeVars();
12411
12412 #if USE_ONE_MORE_CHANGE_PER_FRAME
12413   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12414   {
12415     SCAN_PLAYFIELD(x, y)
12416     {
12417       ChangeCount[x][y] = 0;
12418       ChangeEvent[x][y] = -1;
12419     }
12420   }
12421 #endif
12422
12423   if (game.set_centered_player)
12424   {
12425     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12426
12427     /* switching to "all players" only possible if all players fit to screen */
12428     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12429     {
12430       game.centered_player_nr_next = game.centered_player_nr;
12431       game.set_centered_player = FALSE;
12432     }
12433
12434     /* do not switch focus to non-existing (or non-active) player */
12435     if (game.centered_player_nr_next >= 0 &&
12436         !stored_player[game.centered_player_nr_next].active)
12437     {
12438       game.centered_player_nr_next = game.centered_player_nr;
12439       game.set_centered_player = FALSE;
12440     }
12441   }
12442
12443   if (game.set_centered_player &&
12444       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12445   {
12446     int sx, sy;
12447
12448     if (game.centered_player_nr_next == -1)
12449     {
12450       setScreenCenteredToAllPlayers(&sx, &sy);
12451     }
12452     else
12453     {
12454       sx = stored_player[game.centered_player_nr_next].jx;
12455       sy = stored_player[game.centered_player_nr_next].jy;
12456     }
12457
12458     game.centered_player_nr = game.centered_player_nr_next;
12459     game.set_centered_player = FALSE;
12460
12461     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12462     DrawGameDoorValues();
12463   }
12464
12465   for (i = 0; i < MAX_PLAYERS; i++)
12466   {
12467     int actual_player_action = stored_player[i].effective_action;
12468
12469 #if 1
12470     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12471        - rnd_equinox_tetrachloride 048
12472        - rnd_equinox_tetrachloride_ii 096
12473        - rnd_emanuel_schmieg 002
12474        - doctor_sloan_ww 001, 020
12475     */
12476     if (stored_player[i].MovPos == 0)
12477       CheckGravityMovement(&stored_player[i]);
12478 #endif
12479
12480     /* overwrite programmed action with tape action */
12481     if (stored_player[i].programmed_action)
12482       actual_player_action = stored_player[i].programmed_action;
12483
12484     PlayerActions(&stored_player[i], actual_player_action);
12485
12486     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12487   }
12488
12489   ScrollScreen(NULL, SCROLL_GO_ON);
12490
12491   /* for backwards compatibility, the following code emulates a fixed bug that
12492      occured when pushing elements (causing elements that just made their last
12493      pushing step to already (if possible) make their first falling step in the
12494      same game frame, which is bad); this code is also needed to use the famous
12495      "spring push bug" which is used in older levels and might be wanted to be
12496      used also in newer levels, but in this case the buggy pushing code is only
12497      affecting the "spring" element and no other elements */
12498
12499   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12500   {
12501     for (i = 0; i < MAX_PLAYERS; i++)
12502     {
12503       struct PlayerInfo *player = &stored_player[i];
12504       int x = player->jx;
12505       int y = player->jy;
12506
12507       if (player->active && player->is_pushing && player->is_moving &&
12508           IS_MOVING(x, y) &&
12509           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12510            Feld[x][y] == EL_SPRING))
12511       {
12512         ContinueMoving(x, y);
12513
12514         /* continue moving after pushing (this is actually a bug) */
12515         if (!IS_MOVING(x, y))
12516           Stop[x][y] = FALSE;
12517       }
12518     }
12519   }
12520
12521 #if 0
12522   debug_print_timestamp(0, "start main loop profiling");
12523 #endif
12524
12525   SCAN_PLAYFIELD(x, y)
12526   {
12527     ChangeCount[x][y] = 0;
12528     ChangeEvent[x][y] = -1;
12529
12530     /* this must be handled before main playfield loop */
12531     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12532     {
12533       MovDelay[x][y]--;
12534       if (MovDelay[x][y] <= 0)
12535         RemoveField(x, y);
12536     }
12537
12538 #if USE_NEW_SNAP_DELAY
12539     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12540     {
12541       MovDelay[x][y]--;
12542       if (MovDelay[x][y] <= 0)
12543       {
12544         RemoveField(x, y);
12545         TEST_DrawLevelField(x, y);
12546
12547         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12548       }
12549     }
12550 #endif
12551
12552 #if DEBUG
12553     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12554     {
12555       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12556       printf("GameActions(): This should never happen!\n");
12557
12558       ChangePage[x][y] = -1;
12559     }
12560 #endif
12561
12562     Stop[x][y] = FALSE;
12563     if (WasJustMoving[x][y] > 0)
12564       WasJustMoving[x][y]--;
12565     if (WasJustFalling[x][y] > 0)
12566       WasJustFalling[x][y]--;
12567     if (CheckCollision[x][y] > 0)
12568       CheckCollision[x][y]--;
12569     if (CheckImpact[x][y] > 0)
12570       CheckImpact[x][y]--;
12571
12572     GfxFrame[x][y]++;
12573
12574     /* reset finished pushing action (not done in ContinueMoving() to allow
12575        continuous pushing animation for elements with zero push delay) */
12576     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12577     {
12578       ResetGfxAnimation(x, y);
12579       TEST_DrawLevelField(x, y);
12580     }
12581
12582 #if DEBUG
12583     if (IS_BLOCKED(x, y))
12584     {
12585       int oldx, oldy;
12586
12587       Blocked2Moving(x, y, &oldx, &oldy);
12588       if (!IS_MOVING(oldx, oldy))
12589       {
12590         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12591         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12592         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12593         printf("GameActions(): This should never happen!\n");
12594       }
12595     }
12596 #endif
12597   }
12598
12599 #if 0
12600   debug_print_timestamp(0, "- time for pre-main loop:");
12601 #endif
12602
12603 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12604   SCAN_PLAYFIELD(x, y)
12605   {
12606     element = Feld[x][y];
12607     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12608
12609 #if 1
12610     {
12611 #if 1
12612       int element2 = element;
12613       int graphic2 = graphic;
12614 #else
12615       int element2 = Feld[x][y];
12616       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12617 #endif
12618       int last_gfx_frame = GfxFrame[x][y];
12619
12620       if (graphic_info[graphic2].anim_global_sync)
12621         GfxFrame[x][y] = FrameCounter;
12622       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12623         GfxFrame[x][y] = CustomValue[x][y];
12624       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12625         GfxFrame[x][y] = element_info[element2].collect_score;
12626       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12627         GfxFrame[x][y] = ChangeDelay[x][y];
12628
12629       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12630         DrawLevelGraphicAnimation(x, y, graphic2);
12631     }
12632 #else
12633     ResetGfxFrame(x, y, TRUE);
12634 #endif
12635
12636 #if 1
12637     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12638         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12639       ResetRandomAnimationValue(x, y);
12640 #endif
12641
12642 #if 1
12643     SetRandomAnimationValue(x, y);
12644 #endif
12645
12646 #if 1
12647     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12648 #endif
12649   }
12650 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12651
12652 #if 0
12653   debug_print_timestamp(0, "- time for TEST loop:     -->");
12654 #endif
12655
12656   SCAN_PLAYFIELD(x, y)
12657   {
12658     element = Feld[x][y];
12659     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12660
12661     ResetGfxFrame(x, y, TRUE);
12662
12663     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12664         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12665       ResetRandomAnimationValue(x, y);
12666
12667     SetRandomAnimationValue(x, y);
12668
12669     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12670
12671     if (IS_INACTIVE(element))
12672     {
12673       if (IS_ANIMATED(graphic))
12674         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12675
12676       continue;
12677     }
12678
12679     /* this may take place after moving, so 'element' may have changed */
12680     if (IS_CHANGING(x, y) &&
12681         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12682     {
12683       int page = element_info[element].event_page_nr[CE_DELAY];
12684
12685 #if 1
12686       HandleElementChange(x, y, page);
12687 #else
12688       if (CAN_CHANGE(element))
12689         HandleElementChange(x, y, page);
12690
12691       if (HAS_ACTION(element))
12692         ExecuteCustomElementAction(x, y, element, page);
12693 #endif
12694
12695       element = Feld[x][y];
12696       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12697     }
12698
12699 #if 0   // ---------------------------------------------------------------------
12700
12701     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12702     {
12703       StartMoving(x, y);
12704
12705       element = Feld[x][y];
12706       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12707
12708       if (IS_ANIMATED(graphic) &&
12709           !IS_MOVING(x, y) &&
12710           !Stop[x][y])
12711         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12712
12713       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12714         TEST_DrawTwinkleOnField(x, y);
12715     }
12716     else if (IS_MOVING(x, y))
12717       ContinueMoving(x, y);
12718     else
12719     {
12720       switch (element)
12721       {
12722         case EL_ACID:
12723         case EL_EXIT_OPEN:
12724         case EL_EM_EXIT_OPEN:
12725         case EL_SP_EXIT_OPEN:
12726         case EL_STEEL_EXIT_OPEN:
12727         case EL_EM_STEEL_EXIT_OPEN:
12728         case EL_SP_TERMINAL:
12729         case EL_SP_TERMINAL_ACTIVE:
12730         case EL_EXTRA_TIME:
12731         case EL_SHIELD_NORMAL:
12732         case EL_SHIELD_DEADLY:
12733           if (IS_ANIMATED(graphic))
12734             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12735           break;
12736
12737         case EL_DYNAMITE_ACTIVE:
12738         case EL_EM_DYNAMITE_ACTIVE:
12739         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12740         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12741         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12742         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12743         case EL_SP_DISK_RED_ACTIVE:
12744           CheckDynamite(x, y);
12745           break;
12746
12747         case EL_AMOEBA_GROWING:
12748           AmoebeWaechst(x, y);
12749           break;
12750
12751         case EL_AMOEBA_SHRINKING:
12752           AmoebaDisappearing(x, y);
12753           break;
12754
12755 #if !USE_NEW_AMOEBA_CODE
12756         case EL_AMOEBA_WET:
12757         case EL_AMOEBA_DRY:
12758         case EL_AMOEBA_FULL:
12759         case EL_BD_AMOEBA:
12760         case EL_EMC_DRIPPER:
12761           AmoebeAbleger(x, y);
12762           break;
12763 #endif
12764
12765         case EL_GAME_OF_LIFE:
12766         case EL_BIOMAZE:
12767           Life(x, y);
12768           break;
12769
12770         case EL_EXIT_CLOSED:
12771           CheckExit(x, y);
12772           break;
12773
12774         case EL_EM_EXIT_CLOSED:
12775           CheckExitEM(x, y);
12776           break;
12777
12778         case EL_STEEL_EXIT_CLOSED:
12779           CheckExitSteel(x, y);
12780           break;
12781
12782         case EL_EM_STEEL_EXIT_CLOSED:
12783           CheckExitSteelEM(x, y);
12784           break;
12785
12786         case EL_SP_EXIT_CLOSED:
12787           CheckExitSP(x, y);
12788           break;
12789
12790         case EL_EXPANDABLE_WALL_GROWING:
12791         case EL_EXPANDABLE_STEELWALL_GROWING:
12792           MauerWaechst(x, y);
12793           break;
12794
12795         case EL_EXPANDABLE_WALL:
12796         case EL_EXPANDABLE_WALL_HORIZONTAL:
12797         case EL_EXPANDABLE_WALL_VERTICAL:
12798         case EL_EXPANDABLE_WALL_ANY:
12799         case EL_BD_EXPANDABLE_WALL:
12800           MauerAbleger(x, y);
12801           break;
12802
12803         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12804         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12805         case EL_EXPANDABLE_STEELWALL_ANY:
12806           MauerAblegerStahl(x, y);
12807           break;
12808
12809         case EL_FLAMES:
12810           CheckForDragon(x, y);
12811           break;
12812
12813         case EL_EXPLOSION:
12814           break;
12815
12816         case EL_ELEMENT_SNAPPING:
12817         case EL_DIAGONAL_SHRINKING:
12818         case EL_DIAGONAL_GROWING:
12819         {
12820           graphic =
12821             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12822
12823           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12824           break;
12825         }
12826
12827         default:
12828           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12829             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12830           break;
12831       }
12832     }
12833
12834 #else   // ---------------------------------------------------------------------
12835
12836     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12837     {
12838       StartMoving(x, y);
12839
12840       element = Feld[x][y];
12841       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12842
12843       if (IS_ANIMATED(graphic) &&
12844           !IS_MOVING(x, y) &&
12845           !Stop[x][y])
12846         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12847
12848       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12849         TEST_DrawTwinkleOnField(x, y);
12850     }
12851     else if ((element == EL_ACID ||
12852               element == EL_EXIT_OPEN ||
12853               element == EL_EM_EXIT_OPEN ||
12854               element == EL_SP_EXIT_OPEN ||
12855               element == EL_STEEL_EXIT_OPEN ||
12856               element == EL_EM_STEEL_EXIT_OPEN ||
12857               element == EL_SP_TERMINAL ||
12858               element == EL_SP_TERMINAL_ACTIVE ||
12859               element == EL_EXTRA_TIME ||
12860               element == EL_SHIELD_NORMAL ||
12861               element == EL_SHIELD_DEADLY) &&
12862              IS_ANIMATED(graphic))
12863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12864     else if (IS_MOVING(x, y))
12865       ContinueMoving(x, y);
12866     else if (IS_ACTIVE_BOMB(element))
12867       CheckDynamite(x, y);
12868     else if (element == EL_AMOEBA_GROWING)
12869       AmoebeWaechst(x, y);
12870     else if (element == EL_AMOEBA_SHRINKING)
12871       AmoebaDisappearing(x, y);
12872
12873 #if !USE_NEW_AMOEBA_CODE
12874     else if (IS_AMOEBALIVE(element))
12875       AmoebeAbleger(x, y);
12876 #endif
12877
12878     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12879       Life(x, y);
12880     else if (element == EL_EXIT_CLOSED)
12881       CheckExit(x, y);
12882     else if (element == EL_EM_EXIT_CLOSED)
12883       CheckExitEM(x, y);
12884     else if (element == EL_STEEL_EXIT_CLOSED)
12885       CheckExitSteel(x, y);
12886     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12887       CheckExitSteelEM(x, y);
12888     else if (element == EL_SP_EXIT_CLOSED)
12889       CheckExitSP(x, y);
12890     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12891              element == EL_EXPANDABLE_STEELWALL_GROWING)
12892       MauerWaechst(x, y);
12893     else if (element == EL_EXPANDABLE_WALL ||
12894              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12895              element == EL_EXPANDABLE_WALL_VERTICAL ||
12896              element == EL_EXPANDABLE_WALL_ANY ||
12897              element == EL_BD_EXPANDABLE_WALL)
12898       MauerAbleger(x, y);
12899     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12900              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12901              element == EL_EXPANDABLE_STEELWALL_ANY)
12902       MauerAblegerStahl(x, y);
12903     else if (element == EL_FLAMES)
12904       CheckForDragon(x, y);
12905     else if (element == EL_EXPLOSION)
12906       ; /* drawing of correct explosion animation is handled separately */
12907     else if (element == EL_ELEMENT_SNAPPING ||
12908              element == EL_DIAGONAL_SHRINKING ||
12909              element == EL_DIAGONAL_GROWING)
12910     {
12911       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12912
12913       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12914     }
12915     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12916       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12917
12918 #endif  // ---------------------------------------------------------------------
12919
12920     if (IS_BELT_ACTIVE(element))
12921       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12922
12923     if (game.magic_wall_active)
12924     {
12925       int jx = local_player->jx, jy = local_player->jy;
12926
12927       /* play the element sound at the position nearest to the player */
12928       if ((element == EL_MAGIC_WALL_FULL ||
12929            element == EL_MAGIC_WALL_ACTIVE ||
12930            element == EL_MAGIC_WALL_EMPTYING ||
12931            element == EL_BD_MAGIC_WALL_FULL ||
12932            element == EL_BD_MAGIC_WALL_ACTIVE ||
12933            element == EL_BD_MAGIC_WALL_EMPTYING ||
12934            element == EL_DC_MAGIC_WALL_FULL ||
12935            element == EL_DC_MAGIC_WALL_ACTIVE ||
12936            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12937           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12938       {
12939         magic_wall_x = x;
12940         magic_wall_y = y;
12941       }
12942     }
12943   }
12944
12945 #if 0
12946   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12947 #endif
12948
12949 #if USE_NEW_AMOEBA_CODE
12950   /* new experimental amoeba growth stuff */
12951   if (!(FrameCounter % 8))
12952   {
12953     static unsigned long random = 1684108901;
12954
12955     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12956     {
12957       x = RND(lev_fieldx);
12958       y = RND(lev_fieldy);
12959       element = Feld[x][y];
12960
12961       if (!IS_PLAYER(x,y) &&
12962           (element == EL_EMPTY ||
12963            CAN_GROW_INTO(element) ||
12964            element == EL_QUICKSAND_EMPTY ||
12965            element == EL_QUICKSAND_FAST_EMPTY ||
12966            element == EL_ACID_SPLASH_LEFT ||
12967            element == EL_ACID_SPLASH_RIGHT))
12968       {
12969         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12970             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12971             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12972             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12973           Feld[x][y] = EL_AMOEBA_DROP;
12974       }
12975
12976       random = random * 129 + 1;
12977     }
12978   }
12979 #endif
12980
12981 #if 0
12982   if (game.explosions_delayed)
12983 #endif
12984   {
12985     game.explosions_delayed = FALSE;
12986
12987     SCAN_PLAYFIELD(x, y)
12988     {
12989       element = Feld[x][y];
12990
12991       if (ExplodeField[x][y])
12992         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12993       else if (element == EL_EXPLOSION)
12994         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12995
12996       ExplodeField[x][y] = EX_TYPE_NONE;
12997     }
12998
12999     game.explosions_delayed = TRUE;
13000   }
13001
13002   if (game.magic_wall_active)
13003   {
13004     if (!(game.magic_wall_time_left % 4))
13005     {
13006       int element = Feld[magic_wall_x][magic_wall_y];
13007
13008       if (element == EL_BD_MAGIC_WALL_FULL ||
13009           element == EL_BD_MAGIC_WALL_ACTIVE ||
13010           element == EL_BD_MAGIC_WALL_EMPTYING)
13011         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13012       else if (element == EL_DC_MAGIC_WALL_FULL ||
13013                element == EL_DC_MAGIC_WALL_ACTIVE ||
13014                element == EL_DC_MAGIC_WALL_EMPTYING)
13015         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13016       else
13017         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13018     }
13019
13020     if (game.magic_wall_time_left > 0)
13021     {
13022       game.magic_wall_time_left--;
13023
13024       if (!game.magic_wall_time_left)
13025       {
13026         SCAN_PLAYFIELD(x, y)
13027         {
13028           element = Feld[x][y];
13029
13030           if (element == EL_MAGIC_WALL_ACTIVE ||
13031               element == EL_MAGIC_WALL_FULL)
13032           {
13033             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13034             TEST_DrawLevelField(x, y);
13035           }
13036           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13037                    element == EL_BD_MAGIC_WALL_FULL)
13038           {
13039             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13040             TEST_DrawLevelField(x, y);
13041           }
13042           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13043                    element == EL_DC_MAGIC_WALL_FULL)
13044           {
13045             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13046             TEST_DrawLevelField(x, y);
13047           }
13048         }
13049
13050         game.magic_wall_active = FALSE;
13051       }
13052     }
13053   }
13054
13055   if (game.light_time_left > 0)
13056   {
13057     game.light_time_left--;
13058
13059     if (game.light_time_left == 0)
13060       RedrawAllLightSwitchesAndInvisibleElements();
13061   }
13062
13063   if (game.timegate_time_left > 0)
13064   {
13065     game.timegate_time_left--;
13066
13067     if (game.timegate_time_left == 0)
13068       CloseAllOpenTimegates();
13069   }
13070
13071   if (game.lenses_time_left > 0)
13072   {
13073     game.lenses_time_left--;
13074
13075     if (game.lenses_time_left == 0)
13076       RedrawAllInvisibleElementsForLenses();
13077   }
13078
13079   if (game.magnify_time_left > 0)
13080   {
13081     game.magnify_time_left--;
13082
13083     if (game.magnify_time_left == 0)
13084       RedrawAllInvisibleElementsForMagnifier();
13085   }
13086
13087   for (i = 0; i < MAX_PLAYERS; i++)
13088   {
13089     struct PlayerInfo *player = &stored_player[i];
13090
13091     if (SHIELD_ON(player))
13092     {
13093       if (player->shield_deadly_time_left)
13094         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13095       else if (player->shield_normal_time_left)
13096         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13097     }
13098   }
13099
13100 #if USE_DELAYED_GFX_REDRAW
13101   SCAN_PLAYFIELD(x, y)
13102   {
13103 #if 1
13104     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13105 #else
13106     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13107         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13108 #endif
13109     {
13110       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13111          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13112
13113       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13114         DrawLevelField(x, y);
13115
13116       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13117         DrawLevelFieldCrumbledSand(x, y);
13118
13119       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13120         DrawLevelFieldCrumbledSandNeighbours(x, y);
13121
13122       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13123         DrawTwinkleOnField(x, y);
13124     }
13125
13126     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13127   }
13128 #endif
13129
13130   CheckLevelTime();
13131
13132   DrawAllPlayers();
13133   PlayAllPlayersSound();
13134
13135   if (options.debug)                    /* calculate frames per second */
13136   {
13137     static unsigned long fps_counter = 0;
13138     static int fps_frames = 0;
13139     unsigned long fps_delay_ms = Counter() - fps_counter;
13140
13141     fps_frames++;
13142
13143     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13144     {
13145       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13146
13147       fps_frames = 0;
13148       fps_counter = Counter();
13149     }
13150
13151     redraw_mask |= REDRAW_FPS;
13152   }
13153
13154   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13155
13156   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13157   {
13158     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13159
13160     local_player->show_envelope = 0;
13161   }
13162
13163 #if 0
13164   debug_print_timestamp(0, "stop main loop profiling ");
13165   printf("----------------------------------------------------------\n");
13166 #endif
13167
13168   /* use random number generator in every frame to make it less predictable */
13169   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13170     RND(1);
13171 }
13172
13173 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13174 {
13175   int min_x = x, min_y = y, max_x = x, max_y = y;
13176   int i;
13177
13178   for (i = 0; i < MAX_PLAYERS; i++)
13179   {
13180     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13181
13182     if (!stored_player[i].active || &stored_player[i] == player)
13183       continue;
13184
13185     min_x = MIN(min_x, jx);
13186     min_y = MIN(min_y, jy);
13187     max_x = MAX(max_x, jx);
13188     max_y = MAX(max_y, jy);
13189   }
13190
13191   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13192 }
13193
13194 static boolean AllPlayersInVisibleScreen()
13195 {
13196   int i;
13197
13198   for (i = 0; i < MAX_PLAYERS; i++)
13199   {
13200     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13201
13202     if (!stored_player[i].active)
13203       continue;
13204
13205     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13206       return FALSE;
13207   }
13208
13209   return TRUE;
13210 }
13211
13212 void ScrollLevel(int dx, int dy)
13213 {
13214 #if 0
13215   /* (directly solved in BlitBitmap() now) */
13216   static Bitmap *bitmap_db_field2 = NULL;
13217   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13218   int x, y;
13219 #else
13220   int x, y;
13221 #endif
13222
13223 #if 0
13224   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13225   /* only horizontal XOR vertical scroll direction allowed */
13226   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13227     return;
13228 #endif
13229
13230 #if 0
13231   /* (directly solved in BlitBitmap() now) */
13232   if (bitmap_db_field2 == NULL)
13233     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13234
13235   /* needed when blitting directly to same bitmap -- should not be needed with
13236      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13237   BlitBitmap(drawto_field, bitmap_db_field2,
13238              FX + TILEX * (dx == -1) - softscroll_offset,
13239              FY + TILEY * (dy == -1) - softscroll_offset,
13240              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13241              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13242              FX + TILEX * (dx == 1) - softscroll_offset,
13243              FY + TILEY * (dy == 1) - softscroll_offset);
13244   BlitBitmap(bitmap_db_field2, drawto_field,
13245              FX + TILEX * (dx == 1) - softscroll_offset,
13246              FY + TILEY * (dy == 1) - softscroll_offset,
13247              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13248              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13249              FX + TILEX * (dx == 1) - softscroll_offset,
13250              FY + TILEY * (dy == 1) - softscroll_offset);
13251
13252 #else
13253
13254 #if 0
13255   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13256   int xsize = (BX2 - BX1 + 1);
13257   int ysize = (BY2 - BY1 + 1);
13258   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13259   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13260   int step  = (start < end ? +1 : -1);
13261
13262   for (i = start; i != end; i += step)
13263   {
13264     BlitBitmap(drawto_field, drawto_field,
13265                FX + TILEX * (dx != 0 ? i + step : 0),
13266                FY + TILEY * (dy != 0 ? i + step : 0),
13267                TILEX * (dx != 0 ? 1 : xsize),
13268                TILEY * (dy != 0 ? 1 : ysize),
13269                FX + TILEX * (dx != 0 ? i : 0),
13270                FY + TILEY * (dy != 0 ? i : 0));
13271   }
13272
13273 #else
13274
13275   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13276
13277   BlitBitmap(drawto_field, drawto_field,
13278              FX + TILEX * (dx == -1) - softscroll_offset,
13279              FY + TILEY * (dy == -1) - softscroll_offset,
13280              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13281              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13282              FX + TILEX * (dx == 1) - softscroll_offset,
13283              FY + TILEY * (dy == 1) - softscroll_offset);
13284 #endif
13285 #endif
13286
13287   if (dx != 0)
13288   {
13289     x = (dx == 1 ? BX1 : BX2);
13290     for (y = BY1; y <= BY2; y++)
13291       DrawScreenField(x, y);
13292   }
13293
13294   if (dy != 0)
13295   {
13296     y = (dy == 1 ? BY1 : BY2);
13297     for (x = BX1; x <= BX2; x++)
13298       DrawScreenField(x, y);
13299   }
13300
13301   redraw_mask |= REDRAW_FIELD;
13302 }
13303
13304 static boolean canFallDown(struct PlayerInfo *player)
13305 {
13306   int jx = player->jx, jy = player->jy;
13307
13308   return (IN_LEV_FIELD(jx, jy + 1) &&
13309           (IS_FREE(jx, jy + 1) ||
13310            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13311           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13312           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13313 }
13314
13315 static boolean canPassField(int x, int y, int move_dir)
13316 {
13317   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13318   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13319   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13320   int nextx = x + dx;
13321   int nexty = y + dy;
13322   int element = Feld[x][y];
13323
13324   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13325           !CAN_MOVE(element) &&
13326           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13327           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13328           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13329 }
13330
13331 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13332 {
13333   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13334   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13335   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13336   int newx = x + dx;
13337   int newy = y + dy;
13338
13339   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13340           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13341           (IS_DIGGABLE(Feld[newx][newy]) ||
13342            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13343            canPassField(newx, newy, move_dir)));
13344 }
13345
13346 static void CheckGravityMovement(struct PlayerInfo *player)
13347 {
13348 #if USE_PLAYER_GRAVITY
13349   if (player->gravity && !player->programmed_action)
13350 #else
13351   if (game.gravity && !player->programmed_action)
13352 #endif
13353   {
13354     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13355     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13356     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13357     int jx = player->jx, jy = player->jy;
13358     boolean player_is_moving_to_valid_field =
13359       (!player_is_snapping &&
13360        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13361         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13362     boolean player_can_fall_down = canFallDown(player);
13363
13364     if (player_can_fall_down &&
13365         !player_is_moving_to_valid_field)
13366       player->programmed_action = MV_DOWN;
13367   }
13368 }
13369
13370 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13371 {
13372   return CheckGravityMovement(player);
13373
13374 #if USE_PLAYER_GRAVITY
13375   if (player->gravity && !player->programmed_action)
13376 #else
13377   if (game.gravity && !player->programmed_action)
13378 #endif
13379   {
13380     int jx = player->jx, jy = player->jy;
13381     boolean field_under_player_is_free =
13382       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13383     boolean player_is_standing_on_valid_field =
13384       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13385        (IS_WALKABLE(Feld[jx][jy]) &&
13386         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13387
13388     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13389       player->programmed_action = MV_DOWN;
13390   }
13391 }
13392
13393 /*
13394   MovePlayerOneStep()
13395   -----------------------------------------------------------------------------
13396   dx, dy:               direction (non-diagonal) to try to move the player to
13397   real_dx, real_dy:     direction as read from input device (can be diagonal)
13398 */
13399
13400 boolean MovePlayerOneStep(struct PlayerInfo *player,
13401                           int dx, int dy, int real_dx, int real_dy)
13402 {
13403   int jx = player->jx, jy = player->jy;
13404   int new_jx = jx + dx, new_jy = jy + dy;
13405 #if !USE_FIXED_DONT_RUN_INTO
13406   int element;
13407 #endif
13408   int can_move;
13409   boolean player_can_move = !player->cannot_move;
13410
13411   if (!player->active || (!dx && !dy))
13412     return MP_NO_ACTION;
13413
13414   player->MovDir = (dx < 0 ? MV_LEFT :
13415                     dx > 0 ? MV_RIGHT :
13416                     dy < 0 ? MV_UP :
13417                     dy > 0 ? MV_DOWN :  MV_NONE);
13418
13419   if (!IN_LEV_FIELD(new_jx, new_jy))
13420     return MP_NO_ACTION;
13421
13422   if (!player_can_move)
13423   {
13424     if (player->MovPos == 0)
13425     {
13426       player->is_moving = FALSE;
13427       player->is_digging = FALSE;
13428       player->is_collecting = FALSE;
13429       player->is_snapping = FALSE;
13430       player->is_pushing = FALSE;
13431     }
13432   }
13433
13434 #if 1
13435   if (!options.network && game.centered_player_nr == -1 &&
13436       !AllPlayersInSight(player, new_jx, new_jy))
13437     return MP_NO_ACTION;
13438 #else
13439   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13440     return MP_NO_ACTION;
13441 #endif
13442
13443 #if !USE_FIXED_DONT_RUN_INTO
13444   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13445
13446   /* (moved to DigField()) */
13447   if (player_can_move && DONT_RUN_INTO(element))
13448   {
13449     if (element == EL_ACID && dx == 0 && dy == 1)
13450     {
13451       SplashAcid(new_jx, new_jy);
13452       Feld[jx][jy] = EL_PLAYER_1;
13453       InitMovingField(jx, jy, MV_DOWN);
13454       Store[jx][jy] = EL_ACID;
13455       ContinueMoving(jx, jy);
13456       BuryPlayer(player);
13457     }
13458     else
13459       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13460
13461     return MP_MOVING;
13462   }
13463 #endif
13464
13465   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13466   if (can_move != MP_MOVING)
13467     return can_move;
13468
13469   /* check if DigField() has caused relocation of the player */
13470   if (player->jx != jx || player->jy != jy)
13471     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13472
13473   StorePlayer[jx][jy] = 0;
13474   player->last_jx = jx;
13475   player->last_jy = jy;
13476   player->jx = new_jx;
13477   player->jy = new_jy;
13478   StorePlayer[new_jx][new_jy] = player->element_nr;
13479
13480   if (player->move_delay_value_next != -1)
13481   {
13482     player->move_delay_value = player->move_delay_value_next;
13483     player->move_delay_value_next = -1;
13484   }
13485
13486   player->MovPos =
13487     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13488
13489   player->step_counter++;
13490
13491   PlayerVisit[jx][jy] = FrameCounter;
13492
13493 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13494   player->is_moving = TRUE;
13495 #endif
13496
13497 #if 1
13498   /* should better be called in MovePlayer(), but this breaks some tapes */
13499   ScrollPlayer(player, SCROLL_INIT);
13500 #endif
13501
13502   return MP_MOVING;
13503 }
13504
13505 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13506 {
13507   int jx = player->jx, jy = player->jy;
13508   int old_jx = jx, old_jy = jy;
13509   int moved = MP_NO_ACTION;
13510
13511   if (!player->active)
13512     return FALSE;
13513
13514   if (!dx && !dy)
13515   {
13516     if (player->MovPos == 0)
13517     {
13518       player->is_moving = FALSE;
13519       player->is_digging = FALSE;
13520       player->is_collecting = FALSE;
13521       player->is_snapping = FALSE;
13522       player->is_pushing = FALSE;
13523     }
13524
13525     return FALSE;
13526   }
13527
13528   if (player->move_delay > 0)
13529     return FALSE;
13530
13531   player->move_delay = -1;              /* set to "uninitialized" value */
13532
13533   /* store if player is automatically moved to next field */
13534   player->is_auto_moving = (player->programmed_action != MV_NONE);
13535
13536   /* remove the last programmed player action */
13537   player->programmed_action = 0;
13538
13539   if (player->MovPos)
13540   {
13541     /* should only happen if pre-1.2 tape recordings are played */
13542     /* this is only for backward compatibility */
13543
13544     int original_move_delay_value = player->move_delay_value;
13545
13546 #if DEBUG
13547     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13548            tape.counter);
13549 #endif
13550
13551     /* scroll remaining steps with finest movement resolution */
13552     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13553
13554     while (player->MovPos)
13555     {
13556       ScrollPlayer(player, SCROLL_GO_ON);
13557       ScrollScreen(NULL, SCROLL_GO_ON);
13558
13559       AdvanceFrameAndPlayerCounters(player->index_nr);
13560
13561       DrawAllPlayers();
13562       BackToFront();
13563     }
13564
13565     player->move_delay_value = original_move_delay_value;
13566   }
13567
13568   player->is_active = FALSE;
13569
13570   if (player->last_move_dir & MV_HORIZONTAL)
13571   {
13572     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13573       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13574   }
13575   else
13576   {
13577     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13578       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13579   }
13580
13581 #if USE_FIXED_BORDER_RUNNING_GFX
13582   if (!moved && !player->is_active)
13583   {
13584     player->is_moving = FALSE;
13585     player->is_digging = FALSE;
13586     player->is_collecting = FALSE;
13587     player->is_snapping = FALSE;
13588     player->is_pushing = FALSE;
13589   }
13590 #endif
13591
13592   jx = player->jx;
13593   jy = player->jy;
13594
13595 #if 1
13596   if (moved & MP_MOVING && !ScreenMovPos &&
13597       (player->index_nr == game.centered_player_nr ||
13598        game.centered_player_nr == -1))
13599 #else
13600   if (moved & MP_MOVING && !ScreenMovPos &&
13601       (player == local_player || !options.network))
13602 #endif
13603   {
13604     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13605     int offset = game.scroll_delay_value;
13606
13607     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13608     {
13609       /* actual player has left the screen -- scroll in that direction */
13610       if (jx != old_jx)         /* player has moved horizontally */
13611         scroll_x += (jx - old_jx);
13612       else                      /* player has moved vertically */
13613         scroll_y += (jy - old_jy);
13614     }
13615     else
13616     {
13617       if (jx != old_jx)         /* player has moved horizontally */
13618       {
13619         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13620             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13621           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13622
13623         /* don't scroll over playfield boundaries */
13624         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13625           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13626
13627         /* don't scroll more than one field at a time */
13628         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13629
13630         /* don't scroll against the player's moving direction */
13631         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13632             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13633           scroll_x = old_scroll_x;
13634       }
13635       else                      /* player has moved vertically */
13636       {
13637         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13638             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13639           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13640
13641         /* don't scroll over playfield boundaries */
13642         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13643           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13644
13645         /* don't scroll more than one field at a time */
13646         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13647
13648         /* don't scroll against the player's moving direction */
13649         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13650             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13651           scroll_y = old_scroll_y;
13652       }
13653     }
13654
13655     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13656     {
13657 #if 1
13658       if (!options.network && game.centered_player_nr == -1 &&
13659           !AllPlayersInVisibleScreen())
13660       {
13661         scroll_x = old_scroll_x;
13662         scroll_y = old_scroll_y;
13663       }
13664       else
13665 #else
13666       if (!options.network && !AllPlayersInVisibleScreen())
13667       {
13668         scroll_x = old_scroll_x;
13669         scroll_y = old_scroll_y;
13670       }
13671       else
13672 #endif
13673       {
13674         ScrollScreen(player, SCROLL_INIT);
13675         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13676       }
13677     }
13678   }
13679
13680   player->StepFrame = 0;
13681
13682   if (moved & MP_MOVING)
13683   {
13684     if (old_jx != jx && old_jy == jy)
13685       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13686     else if (old_jx == jx && old_jy != jy)
13687       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13688
13689     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13690
13691     player->last_move_dir = player->MovDir;
13692     player->is_moving = TRUE;
13693     player->is_snapping = FALSE;
13694     player->is_switching = FALSE;
13695     player->is_dropping = FALSE;
13696     player->is_dropping_pressed = FALSE;
13697     player->drop_pressed_delay = 0;
13698
13699 #if 0
13700     /* should better be called here than above, but this breaks some tapes */
13701     ScrollPlayer(player, SCROLL_INIT);
13702 #endif
13703   }
13704   else
13705   {
13706     CheckGravityMovementWhenNotMoving(player);
13707
13708     player->is_moving = FALSE;
13709
13710     /* at this point, the player is allowed to move, but cannot move right now
13711        (e.g. because of something blocking the way) -- ensure that the player
13712        is also allowed to move in the next frame (in old versions before 3.1.1,
13713        the player was forced to wait again for eight frames before next try) */
13714
13715     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13716       player->move_delay = 0;   /* allow direct movement in the next frame */
13717   }
13718
13719   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13720     player->move_delay = player->move_delay_value;
13721
13722   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13723   {
13724     TestIfPlayerTouchesBadThing(jx, jy);
13725     TestIfPlayerTouchesCustomElement(jx, jy);
13726   }
13727
13728   if (!player->active)
13729     RemovePlayer(player);
13730
13731   return moved;
13732 }
13733
13734 void ScrollPlayer(struct PlayerInfo *player, int mode)
13735 {
13736   int jx = player->jx, jy = player->jy;
13737   int last_jx = player->last_jx, last_jy = player->last_jy;
13738   int move_stepsize = TILEX / player->move_delay_value;
13739
13740 #if USE_NEW_PLAYER_SPEED
13741   if (!player->active)
13742     return;
13743
13744   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13745     return;
13746 #else
13747   if (!player->active || player->MovPos == 0)
13748     return;
13749 #endif
13750
13751   if (mode == SCROLL_INIT)
13752   {
13753     player->actual_frame_counter = FrameCounter;
13754     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13755
13756     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13757         Feld[last_jx][last_jy] == EL_EMPTY)
13758     {
13759       int last_field_block_delay = 0;   /* start with no blocking at all */
13760       int block_delay_adjustment = player->block_delay_adjustment;
13761
13762       /* if player blocks last field, add delay for exactly one move */
13763       if (player->block_last_field)
13764       {
13765         last_field_block_delay += player->move_delay_value;
13766
13767         /* when blocking enabled, prevent moving up despite gravity */
13768 #if USE_PLAYER_GRAVITY
13769         if (player->gravity && player->MovDir == MV_UP)
13770           block_delay_adjustment = -1;
13771 #else
13772         if (game.gravity && player->MovDir == MV_UP)
13773           block_delay_adjustment = -1;
13774 #endif
13775       }
13776
13777       /* add block delay adjustment (also possible when not blocking) */
13778       last_field_block_delay += block_delay_adjustment;
13779
13780       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13781       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13782     }
13783
13784 #if USE_NEW_PLAYER_SPEED
13785     if (player->MovPos != 0)    /* player has not yet reached destination */
13786       return;
13787 #else
13788     return;
13789 #endif
13790   }
13791   else if (!FrameReached(&player->actual_frame_counter, 1))
13792     return;
13793
13794 #if USE_NEW_PLAYER_SPEED
13795   if (player->MovPos != 0)
13796   {
13797     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13798     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13799
13800     /* before DrawPlayer() to draw correct player graphic for this case */
13801     if (player->MovPos == 0)
13802       CheckGravityMovement(player);
13803   }
13804 #else
13805   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13806   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13807
13808   /* before DrawPlayer() to draw correct player graphic for this case */
13809   if (player->MovPos == 0)
13810     CheckGravityMovement(player);
13811 #endif
13812
13813   if (player->MovPos == 0)      /* player reached destination field */
13814   {
13815     if (player->move_delay_reset_counter > 0)
13816     {
13817       player->move_delay_reset_counter--;
13818
13819       if (player->move_delay_reset_counter == 0)
13820       {
13821         /* continue with normal speed after quickly moving through gate */
13822         HALVE_PLAYER_SPEED(player);
13823
13824         /* be able to make the next move without delay */
13825         player->move_delay = 0;
13826       }
13827     }
13828
13829     player->last_jx = jx;
13830     player->last_jy = jy;
13831
13832     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13833         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13834 #if 1
13835         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13836 #endif
13837         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13838         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13839 #if 1
13840         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13841 #endif
13842         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13843         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13844     {
13845       DrawPlayer(player);       /* needed here only to cleanup last field */
13846       RemovePlayer(player);
13847
13848       if (local_player->friends_still_needed == 0 ||
13849           IS_SP_ELEMENT(Feld[jx][jy]))
13850         PlayerWins(player);
13851     }
13852
13853     /* this breaks one level: "machine", level 000 */
13854     {
13855       int move_direction = player->MovDir;
13856       int enter_side = MV_DIR_OPPOSITE(move_direction);
13857       int leave_side = move_direction;
13858       int old_jx = last_jx;
13859       int old_jy = last_jy;
13860       int old_element = Feld[old_jx][old_jy];
13861       int new_element = Feld[jx][jy];
13862
13863       if (IS_CUSTOM_ELEMENT(old_element))
13864         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13865                                    CE_LEFT_BY_PLAYER,
13866                                    player->index_bit, leave_side);
13867
13868       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13869                                           CE_PLAYER_LEAVES_X,
13870                                           player->index_bit, leave_side);
13871
13872       if (IS_CUSTOM_ELEMENT(new_element))
13873         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13874                                    player->index_bit, enter_side);
13875
13876       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13877                                           CE_PLAYER_ENTERS_X,
13878                                           player->index_bit, enter_side);
13879
13880 #if USE_FIX_CE_ACTION_WITH_PLAYER
13881       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13882                                         CE_MOVE_OF_X, move_direction);
13883 #else
13884       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13885                                         CE_MOVE_OF_X, move_direction);
13886 #endif
13887     }
13888
13889     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13890     {
13891       TestIfPlayerTouchesBadThing(jx, jy);
13892       TestIfPlayerTouchesCustomElement(jx, jy);
13893
13894       /* needed because pushed element has not yet reached its destination,
13895          so it would trigger a change event at its previous field location */
13896       if (!player->is_pushing)
13897         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13898
13899       if (!player->active)
13900         RemovePlayer(player);
13901     }
13902
13903     if (!local_player->LevelSolved && level.use_step_counter)
13904     {
13905       int i;
13906
13907       TimePlayed++;
13908
13909       if (TimeLeft > 0)
13910       {
13911         TimeLeft--;
13912
13913         if (TimeLeft <= 10 && setup.time_limit)
13914           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13915
13916 #if 1
13917         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13918
13919         DisplayGameControlValues();
13920 #else
13921         DrawGameValue_Time(TimeLeft);
13922 #endif
13923
13924         if (!TimeLeft && setup.time_limit)
13925           for (i = 0; i < MAX_PLAYERS; i++)
13926             KillPlayer(&stored_player[i]);
13927       }
13928 #if 1
13929       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13930       {
13931         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13932
13933         DisplayGameControlValues();
13934       }
13935 #else
13936       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13937         DrawGameValue_Time(TimePlayed);
13938 #endif
13939     }
13940
13941     if (tape.single_step && tape.recording && !tape.pausing &&
13942         !player->programmed_action)
13943       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13944   }
13945 }
13946
13947 void ScrollScreen(struct PlayerInfo *player, int mode)
13948 {
13949   static unsigned long screen_frame_counter = 0;
13950
13951   if (mode == SCROLL_INIT)
13952   {
13953     /* set scrolling step size according to actual player's moving speed */
13954     ScrollStepSize = TILEX / player->move_delay_value;
13955
13956     screen_frame_counter = FrameCounter;
13957     ScreenMovDir = player->MovDir;
13958     ScreenMovPos = player->MovPos;
13959     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13960     return;
13961   }
13962   else if (!FrameReached(&screen_frame_counter, 1))
13963     return;
13964
13965   if (ScreenMovPos)
13966   {
13967     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13968     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13969     redraw_mask |= REDRAW_FIELD;
13970   }
13971   else
13972     ScreenMovDir = MV_NONE;
13973 }
13974
13975 void TestIfPlayerTouchesCustomElement(int x, int y)
13976 {
13977   static int xy[4][2] =
13978   {
13979     { 0, -1 },
13980     { -1, 0 },
13981     { +1, 0 },
13982     { 0, +1 }
13983   };
13984   static int trigger_sides[4][2] =
13985   {
13986     /* center side       border side */
13987     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13988     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13989     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13990     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13991   };
13992   static int touch_dir[4] =
13993   {
13994     MV_LEFT | MV_RIGHT,
13995     MV_UP   | MV_DOWN,
13996     MV_UP   | MV_DOWN,
13997     MV_LEFT | MV_RIGHT
13998   };
13999   int center_element = Feld[x][y];      /* should always be non-moving! */
14000   int i;
14001
14002   for (i = 0; i < NUM_DIRECTIONS; i++)
14003   {
14004     int xx = x + xy[i][0];
14005     int yy = y + xy[i][1];
14006     int center_side = trigger_sides[i][0];
14007     int border_side = trigger_sides[i][1];
14008     int border_element;
14009
14010     if (!IN_LEV_FIELD(xx, yy))
14011       continue;
14012
14013     if (IS_PLAYER(x, y))                /* player found at center element */
14014     {
14015       struct PlayerInfo *player = PLAYERINFO(x, y);
14016
14017       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14018         border_element = Feld[xx][yy];          /* may be moving! */
14019       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14020         border_element = Feld[xx][yy];
14021       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14022         border_element = MovingOrBlocked2Element(xx, yy);
14023       else
14024         continue;               /* center and border element do not touch */
14025
14026       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14027                                  player->index_bit, border_side);
14028       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14029                                           CE_PLAYER_TOUCHES_X,
14030                                           player->index_bit, border_side);
14031
14032 #if USE_FIX_CE_ACTION_WITH_PLAYER
14033       {
14034         /* use player element that is initially defined in the level playfield,
14035            not the player element that corresponds to the runtime player number
14036            (example: a level that contains EL_PLAYER_3 as the only player would
14037            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14038         int player_element = PLAYERINFO(x, y)->initial_element;
14039
14040         CheckElementChangeBySide(xx, yy, border_element, player_element,
14041                                  CE_TOUCHING_X, border_side);
14042       }
14043 #endif
14044     }
14045     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14046     {
14047       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14048
14049       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14050       {
14051         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14052           continue;             /* center and border element do not touch */
14053       }
14054
14055       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14056                                  player->index_bit, center_side);
14057       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14058                                           CE_PLAYER_TOUCHES_X,
14059                                           player->index_bit, center_side);
14060
14061 #if USE_FIX_CE_ACTION_WITH_PLAYER
14062       {
14063         /* use player element that is initially defined in the level playfield,
14064            not the player element that corresponds to the runtime player number
14065            (example: a level that contains EL_PLAYER_3 as the only player would
14066            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14067         int player_element = PLAYERINFO(xx, yy)->initial_element;
14068
14069         CheckElementChangeBySide(x, y, center_element, player_element,
14070                                  CE_TOUCHING_X, center_side);
14071       }
14072 #endif
14073
14074       break;
14075     }
14076   }
14077 }
14078
14079 #if USE_ELEMENT_TOUCHING_BUGFIX
14080
14081 void TestIfElementTouchesCustomElement(int x, int y)
14082 {
14083   static int xy[4][2] =
14084   {
14085     { 0, -1 },
14086     { -1, 0 },
14087     { +1, 0 },
14088     { 0, +1 }
14089   };
14090   static int trigger_sides[4][2] =
14091   {
14092     /* center side      border side */
14093     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14094     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14095     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14096     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14097   };
14098   static int touch_dir[4] =
14099   {
14100     MV_LEFT | MV_RIGHT,
14101     MV_UP   | MV_DOWN,
14102     MV_UP   | MV_DOWN,
14103     MV_LEFT | MV_RIGHT
14104   };
14105   boolean change_center_element = FALSE;
14106   int center_element = Feld[x][y];      /* should always be non-moving! */
14107   int border_element_old[NUM_DIRECTIONS];
14108   int i;
14109
14110   for (i = 0; i < NUM_DIRECTIONS; i++)
14111   {
14112     int xx = x + xy[i][0];
14113     int yy = y + xy[i][1];
14114     int border_element;
14115
14116     border_element_old[i] = -1;
14117
14118     if (!IN_LEV_FIELD(xx, yy))
14119       continue;
14120
14121     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14122       border_element = Feld[xx][yy];    /* may be moving! */
14123     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14124       border_element = Feld[xx][yy];
14125     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14126       border_element = MovingOrBlocked2Element(xx, yy);
14127     else
14128       continue;                 /* center and border element do not touch */
14129
14130     border_element_old[i] = border_element;
14131   }
14132
14133   for (i = 0; i < NUM_DIRECTIONS; i++)
14134   {
14135     int xx = x + xy[i][0];
14136     int yy = y + xy[i][1];
14137     int center_side = trigger_sides[i][0];
14138     int border_element = border_element_old[i];
14139
14140     if (border_element == -1)
14141       continue;
14142
14143     /* check for change of border element */
14144     CheckElementChangeBySide(xx, yy, border_element, center_element,
14145                              CE_TOUCHING_X, center_side);
14146
14147     /* (center element cannot be player, so we dont have to check this here) */
14148   }
14149
14150   for (i = 0; i < NUM_DIRECTIONS; i++)
14151   {
14152     int xx = x + xy[i][0];
14153     int yy = y + xy[i][1];
14154     int border_side = trigger_sides[i][1];
14155     int border_element = border_element_old[i];
14156
14157     if (border_element == -1)
14158       continue;
14159
14160     /* check for change of center element (but change it only once) */
14161     if (!change_center_element)
14162       change_center_element =
14163         CheckElementChangeBySide(x, y, center_element, border_element,
14164                                  CE_TOUCHING_X, border_side);
14165
14166 #if USE_FIX_CE_ACTION_WITH_PLAYER
14167     if (IS_PLAYER(xx, yy))
14168     {
14169       /* use player element that is initially defined in the level playfield,
14170          not the player element that corresponds to the runtime player number
14171          (example: a level that contains EL_PLAYER_3 as the only player would
14172          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14173       int player_element = PLAYERINFO(xx, yy)->initial_element;
14174
14175       CheckElementChangeBySide(x, y, center_element, player_element,
14176                                CE_TOUCHING_X, border_side);
14177     }
14178 #endif
14179   }
14180 }
14181
14182 #else
14183
14184 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14185 {
14186   static int xy[4][2] =
14187   {
14188     { 0, -1 },
14189     { -1, 0 },
14190     { +1, 0 },
14191     { 0, +1 }
14192   };
14193   static int trigger_sides[4][2] =
14194   {
14195     /* center side      border side */
14196     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14197     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14198     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14199     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14200   };
14201   static int touch_dir[4] =
14202   {
14203     MV_LEFT | MV_RIGHT,
14204     MV_UP   | MV_DOWN,
14205     MV_UP   | MV_DOWN,
14206     MV_LEFT | MV_RIGHT
14207   };
14208   boolean change_center_element = FALSE;
14209   int center_element = Feld[x][y];      /* should always be non-moving! */
14210   int i;
14211
14212   for (i = 0; i < NUM_DIRECTIONS; i++)
14213   {
14214     int xx = x + xy[i][0];
14215     int yy = y + xy[i][1];
14216     int center_side = trigger_sides[i][0];
14217     int border_side = trigger_sides[i][1];
14218     int border_element;
14219
14220     if (!IN_LEV_FIELD(xx, yy))
14221       continue;
14222
14223     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14224       border_element = Feld[xx][yy];    /* may be moving! */
14225     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14226       border_element = Feld[xx][yy];
14227     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14228       border_element = MovingOrBlocked2Element(xx, yy);
14229     else
14230       continue;                 /* center and border element do not touch */
14231
14232     /* check for change of center element (but change it only once) */
14233     if (!change_center_element)
14234       change_center_element =
14235         CheckElementChangeBySide(x, y, center_element, border_element,
14236                                  CE_TOUCHING_X, border_side);
14237
14238     /* check for change of border element */
14239     CheckElementChangeBySide(xx, yy, border_element, center_element,
14240                              CE_TOUCHING_X, center_side);
14241   }
14242 }
14243
14244 #endif
14245
14246 void TestIfElementHitsCustomElement(int x, int y, int direction)
14247 {
14248   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14249   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14250   int hitx = x + dx, hity = y + dy;
14251   int hitting_element = Feld[x][y];
14252   int touched_element;
14253
14254   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14255     return;
14256
14257   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14258                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14259
14260   if (IN_LEV_FIELD(hitx, hity))
14261   {
14262     int opposite_direction = MV_DIR_OPPOSITE(direction);
14263     int hitting_side = direction;
14264     int touched_side = opposite_direction;
14265     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14266                           MovDir[hitx][hity] != direction ||
14267                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14268
14269     object_hit = TRUE;
14270
14271     if (object_hit)
14272     {
14273       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14274                                CE_HITTING_X, touched_side);
14275
14276       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14277                                CE_HIT_BY_X, hitting_side);
14278
14279       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14280                                CE_HIT_BY_SOMETHING, opposite_direction);
14281
14282 #if USE_FIX_CE_ACTION_WITH_PLAYER
14283       if (IS_PLAYER(hitx, hity))
14284       {
14285         /* use player element that is initially defined in the level playfield,
14286            not the player element that corresponds to the runtime player number
14287            (example: a level that contains EL_PLAYER_3 as the only player would
14288            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14289         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14290
14291         CheckElementChangeBySide(x, y, hitting_element, player_element,
14292                                  CE_HITTING_X, touched_side);
14293       }
14294 #endif
14295     }
14296   }
14297
14298   /* "hitting something" is also true when hitting the playfield border */
14299   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14300                            CE_HITTING_SOMETHING, direction);
14301 }
14302
14303 #if 0
14304 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14305 {
14306   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14307   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14308   int hitx = x + dx, hity = y + dy;
14309   int hitting_element = Feld[x][y];
14310   int touched_element;
14311 #if 0
14312   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14313                         !IS_FREE(hitx, hity) &&
14314                         (!IS_MOVING(hitx, hity) ||
14315                          MovDir[hitx][hity] != direction ||
14316                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14317 #endif
14318
14319   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14320     return;
14321
14322 #if 0
14323   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14324     return;
14325 #endif
14326
14327   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14328                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14329
14330   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14331                            EP_CAN_SMASH_EVERYTHING, direction);
14332
14333   if (IN_LEV_FIELD(hitx, hity))
14334   {
14335     int opposite_direction = MV_DIR_OPPOSITE(direction);
14336     int hitting_side = direction;
14337     int touched_side = opposite_direction;
14338 #if 0
14339     int touched_element = MovingOrBlocked2Element(hitx, hity);
14340 #endif
14341 #if 1
14342     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14343                           MovDir[hitx][hity] != direction ||
14344                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14345
14346     object_hit = TRUE;
14347 #endif
14348
14349     if (object_hit)
14350     {
14351       int i;
14352
14353       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14354                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14355
14356       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14357                                CE_OTHER_IS_SMASHING, touched_side);
14358
14359       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14360                                CE_OTHER_GETS_SMASHED, hitting_side);
14361     }
14362   }
14363 }
14364 #endif
14365
14366 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14367 {
14368   int i, kill_x = -1, kill_y = -1;
14369
14370   int bad_element = -1;
14371   static int test_xy[4][2] =
14372   {
14373     { 0, -1 },
14374     { -1, 0 },
14375     { +1, 0 },
14376     { 0, +1 }
14377   };
14378   static int test_dir[4] =
14379   {
14380     MV_UP,
14381     MV_LEFT,
14382     MV_RIGHT,
14383     MV_DOWN
14384   };
14385
14386   for (i = 0; i < NUM_DIRECTIONS; i++)
14387   {
14388     int test_x, test_y, test_move_dir, test_element;
14389
14390     test_x = good_x + test_xy[i][0];
14391     test_y = good_y + test_xy[i][1];
14392
14393     if (!IN_LEV_FIELD(test_x, test_y))
14394       continue;
14395
14396     test_move_dir =
14397       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14398
14399     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14400
14401     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14402        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14403     */
14404     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14405         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14406     {
14407       kill_x = test_x;
14408       kill_y = test_y;
14409       bad_element = test_element;
14410
14411       break;
14412     }
14413   }
14414
14415   if (kill_x != -1 || kill_y != -1)
14416   {
14417     if (IS_PLAYER(good_x, good_y))
14418     {
14419       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14420
14421       if (player->shield_deadly_time_left > 0 &&
14422           !IS_INDESTRUCTIBLE(bad_element))
14423         Bang(kill_x, kill_y);
14424       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14425         KillPlayer(player);
14426     }
14427     else
14428       Bang(good_x, good_y);
14429   }
14430 }
14431
14432 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14433 {
14434   int i, kill_x = -1, kill_y = -1;
14435   int bad_element = Feld[bad_x][bad_y];
14436   static int test_xy[4][2] =
14437   {
14438     { 0, -1 },
14439     { -1, 0 },
14440     { +1, 0 },
14441     { 0, +1 }
14442   };
14443   static int touch_dir[4] =
14444   {
14445     MV_LEFT | MV_RIGHT,
14446     MV_UP   | MV_DOWN,
14447     MV_UP   | MV_DOWN,
14448     MV_LEFT | MV_RIGHT
14449   };
14450   static int test_dir[4] =
14451   {
14452     MV_UP,
14453     MV_LEFT,
14454     MV_RIGHT,
14455     MV_DOWN
14456   };
14457
14458   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14459     return;
14460
14461   for (i = 0; i < NUM_DIRECTIONS; i++)
14462   {
14463     int test_x, test_y, test_move_dir, test_element;
14464
14465     test_x = bad_x + test_xy[i][0];
14466     test_y = bad_y + test_xy[i][1];
14467
14468     if (!IN_LEV_FIELD(test_x, test_y))
14469       continue;
14470
14471     test_move_dir =
14472       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14473
14474     test_element = Feld[test_x][test_y];
14475
14476     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14477        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14478     */
14479     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14480         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14481     {
14482       /* good thing is player or penguin that does not move away */
14483       if (IS_PLAYER(test_x, test_y))
14484       {
14485         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14486
14487         if (bad_element == EL_ROBOT && player->is_moving)
14488           continue;     /* robot does not kill player if he is moving */
14489
14490         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14491         {
14492           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14493             continue;           /* center and border element do not touch */
14494         }
14495
14496         kill_x = test_x;
14497         kill_y = test_y;
14498
14499         break;
14500       }
14501       else if (test_element == EL_PENGUIN)
14502       {
14503         kill_x = test_x;
14504         kill_y = test_y;
14505
14506         break;
14507       }
14508     }
14509   }
14510
14511   if (kill_x != -1 || kill_y != -1)
14512   {
14513     if (IS_PLAYER(kill_x, kill_y))
14514     {
14515       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14516
14517       if (player->shield_deadly_time_left > 0 &&
14518           !IS_INDESTRUCTIBLE(bad_element))
14519         Bang(bad_x, bad_y);
14520       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14521         KillPlayer(player);
14522     }
14523     else
14524       Bang(kill_x, kill_y);
14525   }
14526 }
14527
14528 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14529 {
14530   int bad_element = Feld[bad_x][bad_y];
14531   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14532   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14533   int test_x = bad_x + dx, test_y = bad_y + dy;
14534   int test_move_dir, test_element;
14535   int kill_x = -1, kill_y = -1;
14536
14537   if (!IN_LEV_FIELD(test_x, test_y))
14538     return;
14539
14540   test_move_dir =
14541     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14542
14543   test_element = Feld[test_x][test_y];
14544
14545   if (test_move_dir != bad_move_dir)
14546   {
14547     /* good thing can be player or penguin that does not move away */
14548     if (IS_PLAYER(test_x, test_y))
14549     {
14550       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14551
14552       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14553          player as being hit when he is moving towards the bad thing, because
14554          the "get hit by" condition would be lost after the player stops) */
14555       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14556         return;         /* player moves away from bad thing */
14557
14558       kill_x = test_x;
14559       kill_y = test_y;
14560     }
14561     else if (test_element == EL_PENGUIN)
14562     {
14563       kill_x = test_x;
14564       kill_y = test_y;
14565     }
14566   }
14567
14568   if (kill_x != -1 || kill_y != -1)
14569   {
14570     if (IS_PLAYER(kill_x, kill_y))
14571     {
14572       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14573
14574       if (player->shield_deadly_time_left > 0 &&
14575           !IS_INDESTRUCTIBLE(bad_element))
14576         Bang(bad_x, bad_y);
14577       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14578         KillPlayer(player);
14579     }
14580     else
14581       Bang(kill_x, kill_y);
14582   }
14583 }
14584
14585 void TestIfPlayerTouchesBadThing(int x, int y)
14586 {
14587   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14588 }
14589
14590 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14591 {
14592   TestIfGoodThingHitsBadThing(x, y, move_dir);
14593 }
14594
14595 void TestIfBadThingTouchesPlayer(int x, int y)
14596 {
14597   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14598 }
14599
14600 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14601 {
14602   TestIfBadThingHitsGoodThing(x, y, move_dir);
14603 }
14604
14605 void TestIfFriendTouchesBadThing(int x, int y)
14606 {
14607   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14608 }
14609
14610 void TestIfBadThingTouchesFriend(int x, int y)
14611 {
14612   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14613 }
14614
14615 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14616 {
14617   int i, kill_x = bad_x, kill_y = bad_y;
14618   static int xy[4][2] =
14619   {
14620     { 0, -1 },
14621     { -1, 0 },
14622     { +1, 0 },
14623     { 0, +1 }
14624   };
14625
14626   for (i = 0; i < NUM_DIRECTIONS; i++)
14627   {
14628     int x, y, element;
14629
14630     x = bad_x + xy[i][0];
14631     y = bad_y + xy[i][1];
14632     if (!IN_LEV_FIELD(x, y))
14633       continue;
14634
14635     element = Feld[x][y];
14636     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14637         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14638     {
14639       kill_x = x;
14640       kill_y = y;
14641       break;
14642     }
14643   }
14644
14645   if (kill_x != bad_x || kill_y != bad_y)
14646     Bang(bad_x, bad_y);
14647 }
14648
14649 void KillPlayer(struct PlayerInfo *player)
14650 {
14651   int jx = player->jx, jy = player->jy;
14652
14653   if (!player->active)
14654     return;
14655
14656 #if 0
14657   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14658          player->killed, player->active, player->reanimated);
14659 #endif
14660
14661   /* the following code was introduced to prevent an infinite loop when calling
14662      -> Bang()
14663      -> CheckTriggeredElementChangeExt()
14664      -> ExecuteCustomElementAction()
14665      -> KillPlayer()
14666      -> (infinitely repeating the above sequence of function calls)
14667      which occurs when killing the player while having a CE with the setting
14668      "kill player X when explosion of <player X>"; the solution using a new
14669      field "player->killed" was chosen for backwards compatibility, although
14670      clever use of the fields "player->active" etc. would probably also work */
14671 #if 1
14672   if (player->killed)
14673     return;
14674 #endif
14675
14676   player->killed = TRUE;
14677
14678   /* remove accessible field at the player's position */
14679   Feld[jx][jy] = EL_EMPTY;
14680
14681   /* deactivate shield (else Bang()/Explode() would not work right) */
14682   player->shield_normal_time_left = 0;
14683   player->shield_deadly_time_left = 0;
14684
14685 #if 0
14686   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14687          player->killed, player->active, player->reanimated);
14688 #endif
14689
14690   Bang(jx, jy);
14691
14692 #if 0
14693   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14694          player->killed, player->active, player->reanimated);
14695 #endif
14696
14697 #if USE_PLAYER_REANIMATION
14698 #if 1
14699   if (player->reanimated)       /* killed player may have been reanimated */
14700     player->killed = player->reanimated = FALSE;
14701   else
14702     BuryPlayer(player);
14703 #else
14704   if (player->killed)           /* player may have been reanimated */
14705     BuryPlayer(player);
14706 #endif
14707 #else
14708   BuryPlayer(player);
14709 #endif
14710 }
14711
14712 static void KillPlayerUnlessEnemyProtected(int x, int y)
14713 {
14714   if (!PLAYER_ENEMY_PROTECTED(x, y))
14715     KillPlayer(PLAYERINFO(x, y));
14716 }
14717
14718 static void KillPlayerUnlessExplosionProtected(int x, int y)
14719 {
14720   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14721     KillPlayer(PLAYERINFO(x, y));
14722 }
14723
14724 void BuryPlayer(struct PlayerInfo *player)
14725 {
14726   int jx = player->jx, jy = player->jy;
14727
14728   if (!player->active)
14729     return;
14730
14731   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14732   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14733
14734   player->GameOver = TRUE;
14735   RemovePlayer(player);
14736 }
14737
14738 void RemovePlayer(struct PlayerInfo *player)
14739 {
14740   int jx = player->jx, jy = player->jy;
14741   int i, found = FALSE;
14742
14743   player->present = FALSE;
14744   player->active = FALSE;
14745
14746   if (!ExplodeField[jx][jy])
14747     StorePlayer[jx][jy] = 0;
14748
14749   if (player->is_moving)
14750     TEST_DrawLevelField(player->last_jx, player->last_jy);
14751
14752   for (i = 0; i < MAX_PLAYERS; i++)
14753     if (stored_player[i].active)
14754       found = TRUE;
14755
14756   if (!found)
14757     AllPlayersGone = TRUE;
14758
14759   ExitX = ZX = jx;
14760   ExitY = ZY = jy;
14761 }
14762
14763 #if USE_NEW_SNAP_DELAY
14764 static void setFieldForSnapping(int x, int y, int element, int direction)
14765 {
14766   struct ElementInfo *ei = &element_info[element];
14767   int direction_bit = MV_DIR_TO_BIT(direction);
14768   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14769   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14770                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14771
14772   Feld[x][y] = EL_ELEMENT_SNAPPING;
14773   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14774
14775   ResetGfxAnimation(x, y);
14776
14777   GfxElement[x][y] = element;
14778   GfxAction[x][y] = action;
14779   GfxDir[x][y] = direction;
14780   GfxFrame[x][y] = -1;
14781 }
14782 #endif
14783
14784 /*
14785   =============================================================================
14786   checkDiagonalPushing()
14787   -----------------------------------------------------------------------------
14788   check if diagonal input device direction results in pushing of object
14789   (by checking if the alternative direction is walkable, diggable, ...)
14790   =============================================================================
14791 */
14792
14793 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14794                                     int x, int y, int real_dx, int real_dy)
14795 {
14796   int jx, jy, dx, dy, xx, yy;
14797
14798   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14799     return TRUE;
14800
14801   /* diagonal direction: check alternative direction */
14802   jx = player->jx;
14803   jy = player->jy;
14804   dx = x - jx;
14805   dy = y - jy;
14806   xx = jx + (dx == 0 ? real_dx : 0);
14807   yy = jy + (dy == 0 ? real_dy : 0);
14808
14809   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14810 }
14811
14812 /*
14813   =============================================================================
14814   DigField()
14815   -----------------------------------------------------------------------------
14816   x, y:                 field next to player (non-diagonal) to try to dig to
14817   real_dx, real_dy:     direction as read from input device (can be diagonal)
14818   =============================================================================
14819 */
14820
14821 static int DigField(struct PlayerInfo *player,
14822                     int oldx, int oldy, int x, int y,
14823                     int real_dx, int real_dy, int mode)
14824 {
14825   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14826   boolean player_was_pushing = player->is_pushing;
14827   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14828   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14829   int jx = oldx, jy = oldy;
14830   int dx = x - jx, dy = y - jy;
14831   int nextx = x + dx, nexty = y + dy;
14832   int move_direction = (dx == -1 ? MV_LEFT  :
14833                         dx == +1 ? MV_RIGHT :
14834                         dy == -1 ? MV_UP    :
14835                         dy == +1 ? MV_DOWN  : MV_NONE);
14836   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14837   int dig_side = MV_DIR_OPPOSITE(move_direction);
14838   int old_element = Feld[jx][jy];
14839 #if USE_FIXED_DONT_RUN_INTO
14840   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14841 #else
14842   int element;
14843 #endif
14844   int collect_count;
14845
14846   if (is_player)                /* function can also be called by EL_PENGUIN */
14847   {
14848     if (player->MovPos == 0)
14849     {
14850       player->is_digging = FALSE;
14851       player->is_collecting = FALSE;
14852     }
14853
14854     if (player->MovPos == 0)    /* last pushing move finished */
14855       player->is_pushing = FALSE;
14856
14857     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14858     {
14859       player->is_switching = FALSE;
14860       player->push_delay = -1;
14861
14862       return MP_NO_ACTION;
14863     }
14864   }
14865
14866 #if !USE_FIXED_DONT_RUN_INTO
14867   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14868     return MP_NO_ACTION;
14869 #endif
14870
14871   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14872     old_element = Back[jx][jy];
14873
14874   /* in case of element dropped at player position, check background */
14875   else if (Back[jx][jy] != EL_EMPTY &&
14876            game.engine_version >= VERSION_IDENT(2,2,0,0))
14877     old_element = Back[jx][jy];
14878
14879   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14880     return MP_NO_ACTION;        /* field has no opening in this direction */
14881
14882   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14883     return MP_NO_ACTION;        /* field has no opening in this direction */
14884
14885 #if USE_FIXED_DONT_RUN_INTO
14886   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14887   {
14888     SplashAcid(x, y);
14889
14890     Feld[jx][jy] = player->artwork_element;
14891     InitMovingField(jx, jy, MV_DOWN);
14892     Store[jx][jy] = EL_ACID;
14893     ContinueMoving(jx, jy);
14894     BuryPlayer(player);
14895
14896     return MP_DONT_RUN_INTO;
14897   }
14898 #endif
14899
14900 #if USE_FIXED_DONT_RUN_INTO
14901   if (player_can_move && DONT_RUN_INTO(element))
14902   {
14903     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14904
14905     return MP_DONT_RUN_INTO;
14906   }
14907 #endif
14908
14909 #if USE_FIXED_DONT_RUN_INTO
14910   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14911     return MP_NO_ACTION;
14912 #endif
14913
14914 #if !USE_FIXED_DONT_RUN_INTO
14915   element = Feld[x][y];
14916 #endif
14917
14918   collect_count = element_info[element].collect_count_initial;
14919
14920   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14921     return MP_NO_ACTION;
14922
14923   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14924     player_can_move = player_can_move_or_snap;
14925
14926   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14927       game.engine_version >= VERSION_IDENT(2,2,0,0))
14928   {
14929     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14930                                player->index_bit, dig_side);
14931     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14932                                         player->index_bit, dig_side);
14933
14934     if (element == EL_DC_LANDMINE)
14935       Bang(x, y);
14936
14937     if (Feld[x][y] != element)          /* field changed by snapping */
14938       return MP_ACTION;
14939
14940     return MP_NO_ACTION;
14941   }
14942
14943 #if USE_PLAYER_GRAVITY
14944   if (player->gravity && is_player && !player->is_auto_moving &&
14945       canFallDown(player) && move_direction != MV_DOWN &&
14946       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14947     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14948 #else
14949   if (game.gravity && is_player && !player->is_auto_moving &&
14950       canFallDown(player) && move_direction != MV_DOWN &&
14951       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14952     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14953 #endif
14954
14955   if (player_can_move &&
14956       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14957   {
14958     int sound_element = SND_ELEMENT(element);
14959     int sound_action = ACTION_WALKING;
14960
14961     if (IS_RND_GATE(element))
14962     {
14963       if (!player->key[RND_GATE_NR(element)])
14964         return MP_NO_ACTION;
14965     }
14966     else if (IS_RND_GATE_GRAY(element))
14967     {
14968       if (!player->key[RND_GATE_GRAY_NR(element)])
14969         return MP_NO_ACTION;
14970     }
14971     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14972     {
14973       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14974         return MP_NO_ACTION;
14975     }
14976     else if (element == EL_EXIT_OPEN ||
14977              element == EL_EM_EXIT_OPEN ||
14978 #if 1
14979              element == EL_EM_EXIT_OPENING ||
14980 #endif
14981              element == EL_STEEL_EXIT_OPEN ||
14982              element == EL_EM_STEEL_EXIT_OPEN ||
14983 #if 1
14984              element == EL_EM_STEEL_EXIT_OPENING ||
14985 #endif
14986              element == EL_SP_EXIT_OPEN ||
14987              element == EL_SP_EXIT_OPENING)
14988     {
14989       sound_action = ACTION_PASSING;    /* player is passing exit */
14990     }
14991     else if (element == EL_EMPTY)
14992     {
14993       sound_action = ACTION_MOVING;             /* nothing to walk on */
14994     }
14995
14996     /* play sound from background or player, whatever is available */
14997     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14998       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14999     else
15000       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15001   }
15002   else if (player_can_move &&
15003            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15004   {
15005     if (!ACCESS_FROM(element, opposite_direction))
15006       return MP_NO_ACTION;      /* field not accessible from this direction */
15007
15008     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15009       return MP_NO_ACTION;
15010
15011     if (IS_EM_GATE(element))
15012     {
15013       if (!player->key[EM_GATE_NR(element)])
15014         return MP_NO_ACTION;
15015     }
15016     else if (IS_EM_GATE_GRAY(element))
15017     {
15018       if (!player->key[EM_GATE_GRAY_NR(element)])
15019         return MP_NO_ACTION;
15020     }
15021     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15022     {
15023       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15024         return MP_NO_ACTION;
15025     }
15026     else if (IS_EMC_GATE(element))
15027     {
15028       if (!player->key[EMC_GATE_NR(element)])
15029         return MP_NO_ACTION;
15030     }
15031     else if (IS_EMC_GATE_GRAY(element))
15032     {
15033       if (!player->key[EMC_GATE_GRAY_NR(element)])
15034         return MP_NO_ACTION;
15035     }
15036     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15037     {
15038       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15039         return MP_NO_ACTION;
15040     }
15041     else if (element == EL_DC_GATE_WHITE ||
15042              element == EL_DC_GATE_WHITE_GRAY ||
15043              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15044     {
15045       if (player->num_white_keys == 0)
15046         return MP_NO_ACTION;
15047
15048       player->num_white_keys--;
15049     }
15050     else if (IS_SP_PORT(element))
15051     {
15052       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15053           element == EL_SP_GRAVITY_PORT_RIGHT ||
15054           element == EL_SP_GRAVITY_PORT_UP ||
15055           element == EL_SP_GRAVITY_PORT_DOWN)
15056 #if USE_PLAYER_GRAVITY
15057         player->gravity = !player->gravity;
15058 #else
15059         game.gravity = !game.gravity;
15060 #endif
15061       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15062                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15063                element == EL_SP_GRAVITY_ON_PORT_UP ||
15064                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15065 #if USE_PLAYER_GRAVITY
15066         player->gravity = TRUE;
15067 #else
15068         game.gravity = TRUE;
15069 #endif
15070       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15071                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15072                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15073                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15074 #if USE_PLAYER_GRAVITY
15075         player->gravity = FALSE;
15076 #else
15077         game.gravity = FALSE;
15078 #endif
15079     }
15080
15081     /* automatically move to the next field with double speed */
15082     player->programmed_action = move_direction;
15083
15084     if (player->move_delay_reset_counter == 0)
15085     {
15086       player->move_delay_reset_counter = 2;     /* two double speed steps */
15087
15088       DOUBLE_PLAYER_SPEED(player);
15089     }
15090
15091     PlayLevelSoundAction(x, y, ACTION_PASSING);
15092   }
15093   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15094   {
15095     RemoveField(x, y);
15096
15097     if (mode != DF_SNAP)
15098     {
15099       GfxElement[x][y] = GFX_ELEMENT(element);
15100       player->is_digging = TRUE;
15101     }
15102
15103     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15104
15105     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15106                                         player->index_bit, dig_side);
15107
15108     if (mode == DF_SNAP)
15109     {
15110 #if USE_NEW_SNAP_DELAY
15111       if (level.block_snap_field)
15112         setFieldForSnapping(x, y, element, move_direction);
15113       else
15114         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15115 #else
15116       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15117 #endif
15118
15119       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15120                                           player->index_bit, dig_side);
15121     }
15122   }
15123   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15124   {
15125     RemoveField(x, y);
15126
15127     if (is_player && mode != DF_SNAP)
15128     {
15129       GfxElement[x][y] = element;
15130       player->is_collecting = TRUE;
15131     }
15132
15133     if (element == EL_SPEED_PILL)
15134     {
15135       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15136     }
15137     else if (element == EL_EXTRA_TIME && level.time > 0)
15138     {
15139       TimeLeft += level.extra_time;
15140
15141 #if 1
15142       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15143
15144       DisplayGameControlValues();
15145 #else
15146       DrawGameValue_Time(TimeLeft);
15147 #endif
15148     }
15149     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15150     {
15151       player->shield_normal_time_left += level.shield_normal_time;
15152       if (element == EL_SHIELD_DEADLY)
15153         player->shield_deadly_time_left += level.shield_deadly_time;
15154     }
15155     else if (element == EL_DYNAMITE ||
15156              element == EL_EM_DYNAMITE ||
15157              element == EL_SP_DISK_RED)
15158     {
15159       if (player->inventory_size < MAX_INVENTORY_SIZE)
15160         player->inventory_element[player->inventory_size++] = element;
15161
15162       DrawGameDoorValues();
15163     }
15164     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15165     {
15166       player->dynabomb_count++;
15167       player->dynabombs_left++;
15168     }
15169     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15170     {
15171       player->dynabomb_size++;
15172     }
15173     else if (element == EL_DYNABOMB_INCREASE_POWER)
15174     {
15175       player->dynabomb_xl = TRUE;
15176     }
15177     else if (IS_KEY(element))
15178     {
15179       player->key[KEY_NR(element)] = TRUE;
15180
15181       DrawGameDoorValues();
15182     }
15183     else if (element == EL_DC_KEY_WHITE)
15184     {
15185       player->num_white_keys++;
15186
15187       /* display white keys? */
15188       /* DrawGameDoorValues(); */
15189     }
15190     else if (IS_ENVELOPE(element))
15191     {
15192       player->show_envelope = element;
15193     }
15194     else if (element == EL_EMC_LENSES)
15195     {
15196       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15197
15198       RedrawAllInvisibleElementsForLenses();
15199     }
15200     else if (element == EL_EMC_MAGNIFIER)
15201     {
15202       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15203
15204       RedrawAllInvisibleElementsForMagnifier();
15205     }
15206     else if (IS_DROPPABLE(element) ||
15207              IS_THROWABLE(element))     /* can be collected and dropped */
15208     {
15209       int i;
15210
15211       if (collect_count == 0)
15212         player->inventory_infinite_element = element;
15213       else
15214         for (i = 0; i < collect_count; i++)
15215           if (player->inventory_size < MAX_INVENTORY_SIZE)
15216             player->inventory_element[player->inventory_size++] = element;
15217
15218       DrawGameDoorValues();
15219     }
15220     else if (collect_count > 0)
15221     {
15222       local_player->gems_still_needed -= collect_count;
15223       if (local_player->gems_still_needed < 0)
15224         local_player->gems_still_needed = 0;
15225
15226 #if 1
15227       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15228
15229       DisplayGameControlValues();
15230 #else
15231       DrawGameValue_Emeralds(local_player->gems_still_needed);
15232 #endif
15233     }
15234
15235     RaiseScoreElement(element);
15236     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15237
15238     if (is_player)
15239       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15240                                           player->index_bit, dig_side);
15241
15242     if (mode == DF_SNAP)
15243     {
15244 #if USE_NEW_SNAP_DELAY
15245       if (level.block_snap_field)
15246         setFieldForSnapping(x, y, element, move_direction);
15247       else
15248         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15249 #else
15250       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15251 #endif
15252
15253       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15254                                           player->index_bit, dig_side);
15255     }
15256   }
15257   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15258   {
15259     if (mode == DF_SNAP && element != EL_BD_ROCK)
15260       return MP_NO_ACTION;
15261
15262     if (CAN_FALL(element) && dy)
15263       return MP_NO_ACTION;
15264
15265     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15266         !(element == EL_SPRING && level.use_spring_bug))
15267       return MP_NO_ACTION;
15268
15269     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15270         ((move_direction & MV_VERTICAL &&
15271           ((element_info[element].move_pattern & MV_LEFT &&
15272             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15273            (element_info[element].move_pattern & MV_RIGHT &&
15274             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15275          (move_direction & MV_HORIZONTAL &&
15276           ((element_info[element].move_pattern & MV_UP &&
15277             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15278            (element_info[element].move_pattern & MV_DOWN &&
15279             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15280       return MP_NO_ACTION;
15281
15282     /* do not push elements already moving away faster than player */
15283     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15284         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15285       return MP_NO_ACTION;
15286
15287     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15288     {
15289       if (player->push_delay_value == -1 || !player_was_pushing)
15290         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15291     }
15292     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15293     {
15294       if (player->push_delay_value == -1)
15295         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15296     }
15297     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15298     {
15299       if (!player->is_pushing)
15300         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15301     }
15302
15303     player->is_pushing = TRUE;
15304     player->is_active = TRUE;
15305
15306     if (!(IN_LEV_FIELD(nextx, nexty) &&
15307           (IS_FREE(nextx, nexty) ||
15308            (IS_SB_ELEMENT(element) &&
15309             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15310            (IS_CUSTOM_ELEMENT(element) &&
15311             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15312       return MP_NO_ACTION;
15313
15314     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15315       return MP_NO_ACTION;
15316
15317     if (player->push_delay == -1)       /* new pushing; restart delay */
15318       player->push_delay = 0;
15319
15320     if (player->push_delay < player->push_delay_value &&
15321         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15322         element != EL_SPRING && element != EL_BALLOON)
15323     {
15324       /* make sure that there is no move delay before next try to push */
15325       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15326         player->move_delay = 0;
15327
15328       return MP_NO_ACTION;
15329     }
15330
15331     if (IS_CUSTOM_ELEMENT(element) &&
15332         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15333     {
15334       if (!DigFieldByCE(nextx, nexty, element))
15335         return MP_NO_ACTION;
15336     }
15337
15338     if (IS_SB_ELEMENT(element))
15339     {
15340       if (element == EL_SOKOBAN_FIELD_FULL)
15341       {
15342         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15343         local_player->sokobanfields_still_needed++;
15344       }
15345
15346       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15347       {
15348         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15349         local_player->sokobanfields_still_needed--;
15350       }
15351
15352       Feld[x][y] = EL_SOKOBAN_OBJECT;
15353
15354       if (Back[x][y] == Back[nextx][nexty])
15355         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15356       else if (Back[x][y] != 0)
15357         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15358                                     ACTION_EMPTYING);
15359       else
15360         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15361                                     ACTION_FILLING);
15362
15363       if (local_player->sokobanfields_still_needed == 0 &&
15364           game.emulation == EMU_SOKOBAN)
15365       {
15366         PlayerWins(player);
15367
15368         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15369       }
15370     }
15371     else
15372       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15373
15374     InitMovingField(x, y, move_direction);
15375     GfxAction[x][y] = ACTION_PUSHING;
15376
15377     if (mode == DF_SNAP)
15378       ContinueMoving(x, y);
15379     else
15380       MovPos[x][y] = (dx != 0 ? dx : dy);
15381
15382     Pushed[x][y] = TRUE;
15383     Pushed[nextx][nexty] = TRUE;
15384
15385     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15386       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15387     else
15388       player->push_delay_value = -1;    /* get new value later */
15389
15390     /* check for element change _after_ element has been pushed */
15391     if (game.use_change_when_pushing_bug)
15392     {
15393       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15394                                  player->index_bit, dig_side);
15395       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15396                                           player->index_bit, dig_side);
15397     }
15398   }
15399   else if (IS_SWITCHABLE(element))
15400   {
15401     if (PLAYER_SWITCHING(player, x, y))
15402     {
15403       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15404                                           player->index_bit, dig_side);
15405
15406       return MP_ACTION;
15407     }
15408
15409     player->is_switching = TRUE;
15410     player->switch_x = x;
15411     player->switch_y = y;
15412
15413     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15414
15415     if (element == EL_ROBOT_WHEEL)
15416     {
15417       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15418       ZX = x;
15419       ZY = y;
15420
15421       game.robot_wheel_active = TRUE;
15422
15423       TEST_DrawLevelField(x, y);
15424     }
15425     else if (element == EL_SP_TERMINAL)
15426     {
15427       int xx, yy;
15428
15429       SCAN_PLAYFIELD(xx, yy)
15430       {
15431         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15432           Bang(xx, yy);
15433         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15434           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15435       }
15436     }
15437     else if (IS_BELT_SWITCH(element))
15438     {
15439       ToggleBeltSwitch(x, y);
15440     }
15441     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15442              element == EL_SWITCHGATE_SWITCH_DOWN ||
15443              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15444              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15445     {
15446       ToggleSwitchgateSwitch(x, y);
15447     }
15448     else if (element == EL_LIGHT_SWITCH ||
15449              element == EL_LIGHT_SWITCH_ACTIVE)
15450     {
15451       ToggleLightSwitch(x, y);
15452     }
15453     else if (element == EL_TIMEGATE_SWITCH ||
15454              element == EL_DC_TIMEGATE_SWITCH)
15455     {
15456       ActivateTimegateSwitch(x, y);
15457     }
15458     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15459              element == EL_BALLOON_SWITCH_RIGHT ||
15460              element == EL_BALLOON_SWITCH_UP    ||
15461              element == EL_BALLOON_SWITCH_DOWN  ||
15462              element == EL_BALLOON_SWITCH_NONE  ||
15463              element == EL_BALLOON_SWITCH_ANY)
15464     {
15465       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15466                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15467                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15468                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15469                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15470                              move_direction);
15471     }
15472     else if (element == EL_LAMP)
15473     {
15474       Feld[x][y] = EL_LAMP_ACTIVE;
15475       local_player->lights_still_needed--;
15476
15477       ResetGfxAnimation(x, y);
15478       TEST_DrawLevelField(x, y);
15479     }
15480     else if (element == EL_TIME_ORB_FULL)
15481     {
15482       Feld[x][y] = EL_TIME_ORB_EMPTY;
15483
15484       if (level.time > 0 || level.use_time_orb_bug)
15485       {
15486         TimeLeft += level.time_orb_time;
15487
15488 #if 1
15489         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15490
15491         DisplayGameControlValues();
15492 #else
15493         DrawGameValue_Time(TimeLeft);
15494 #endif
15495       }
15496
15497       ResetGfxAnimation(x, y);
15498       TEST_DrawLevelField(x, y);
15499     }
15500     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15501              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15502     {
15503       int xx, yy;
15504
15505       game.ball_state = !game.ball_state;
15506
15507       SCAN_PLAYFIELD(xx, yy)
15508       {
15509         int e = Feld[xx][yy];
15510
15511         if (game.ball_state)
15512         {
15513           if (e == EL_EMC_MAGIC_BALL)
15514             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15515           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15516             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15517         }
15518         else
15519         {
15520           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15521             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15522           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15523             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15524         }
15525       }
15526     }
15527
15528     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15529                                         player->index_bit, dig_side);
15530
15531     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15532                                         player->index_bit, dig_side);
15533
15534     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15535                                         player->index_bit, dig_side);
15536
15537     return MP_ACTION;
15538   }
15539   else
15540   {
15541     if (!PLAYER_SWITCHING(player, x, y))
15542     {
15543       player->is_switching = TRUE;
15544       player->switch_x = x;
15545       player->switch_y = y;
15546
15547       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15548                                  player->index_bit, dig_side);
15549       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15550                                           player->index_bit, dig_side);
15551
15552       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15553                                  player->index_bit, dig_side);
15554       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15555                                           player->index_bit, dig_side);
15556     }
15557
15558     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15559                                player->index_bit, dig_side);
15560     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15561                                         player->index_bit, dig_side);
15562
15563     return MP_NO_ACTION;
15564   }
15565
15566   player->push_delay = -1;
15567
15568   if (is_player)                /* function can also be called by EL_PENGUIN */
15569   {
15570     if (Feld[x][y] != element)          /* really digged/collected something */
15571     {
15572       player->is_collecting = !player->is_digging;
15573       player->is_active = TRUE;
15574     }
15575   }
15576
15577   return MP_MOVING;
15578 }
15579
15580 static boolean DigFieldByCE(int x, int y, int digging_element)
15581 {
15582   int element = Feld[x][y];
15583
15584   if (!IS_FREE(x, y))
15585   {
15586     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15587                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15588                   ACTION_BREAKING);
15589
15590     /* no element can dig solid indestructible elements */
15591     if (IS_INDESTRUCTIBLE(element) &&
15592         !IS_DIGGABLE(element) &&
15593         !IS_COLLECTIBLE(element))
15594       return FALSE;
15595
15596     if (AmoebaNr[x][y] &&
15597         (element == EL_AMOEBA_FULL ||
15598          element == EL_BD_AMOEBA ||
15599          element == EL_AMOEBA_GROWING))
15600     {
15601       AmoebaCnt[AmoebaNr[x][y]]--;
15602       AmoebaCnt2[AmoebaNr[x][y]]--;
15603     }
15604
15605     if (IS_MOVING(x, y))
15606       RemoveMovingField(x, y);
15607     else
15608     {
15609       RemoveField(x, y);
15610       TEST_DrawLevelField(x, y);
15611     }
15612
15613     /* if digged element was about to explode, prevent the explosion */
15614     ExplodeField[x][y] = EX_TYPE_NONE;
15615
15616     PlayLevelSoundAction(x, y, action);
15617   }
15618
15619   Store[x][y] = EL_EMPTY;
15620
15621 #if 1
15622   /* this makes it possible to leave the removed element again */
15623   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15624     Store[x][y] = element;
15625 #else
15626   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15627   {
15628     int move_leave_element = element_info[digging_element].move_leave_element;
15629
15630     /* this makes it possible to leave the removed element again */
15631     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15632                    element : move_leave_element);
15633   }
15634 #endif
15635
15636   return TRUE;
15637 }
15638
15639 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15640 {
15641   int jx = player->jx, jy = player->jy;
15642   int x = jx + dx, y = jy + dy;
15643   int snap_direction = (dx == -1 ? MV_LEFT  :
15644                         dx == +1 ? MV_RIGHT :
15645                         dy == -1 ? MV_UP    :
15646                         dy == +1 ? MV_DOWN  : MV_NONE);
15647   boolean can_continue_snapping = (level.continuous_snapping &&
15648                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15649
15650   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15651     return FALSE;
15652
15653   if (!player->active || !IN_LEV_FIELD(x, y))
15654     return FALSE;
15655
15656   if (dx && dy)
15657     return FALSE;
15658
15659   if (!dx && !dy)
15660   {
15661     if (player->MovPos == 0)
15662       player->is_pushing = FALSE;
15663
15664     player->is_snapping = FALSE;
15665
15666     if (player->MovPos == 0)
15667     {
15668       player->is_moving = FALSE;
15669       player->is_digging = FALSE;
15670       player->is_collecting = FALSE;
15671     }
15672
15673     return FALSE;
15674   }
15675
15676 #if USE_NEW_CONTINUOUS_SNAPPING
15677   /* prevent snapping with already pressed snap key when not allowed */
15678   if (player->is_snapping && !can_continue_snapping)
15679     return FALSE;
15680 #else
15681   if (player->is_snapping)
15682     return FALSE;
15683 #endif
15684
15685   player->MovDir = snap_direction;
15686
15687   if (player->MovPos == 0)
15688   {
15689     player->is_moving = FALSE;
15690     player->is_digging = FALSE;
15691     player->is_collecting = FALSE;
15692   }
15693
15694   player->is_dropping = FALSE;
15695   player->is_dropping_pressed = FALSE;
15696   player->drop_pressed_delay = 0;
15697
15698   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15699     return FALSE;
15700
15701   player->is_snapping = TRUE;
15702   player->is_active = TRUE;
15703
15704   if (player->MovPos == 0)
15705   {
15706     player->is_moving = FALSE;
15707     player->is_digging = FALSE;
15708     player->is_collecting = FALSE;
15709   }
15710
15711   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15712     TEST_DrawLevelField(player->last_jx, player->last_jy);
15713
15714   TEST_DrawLevelField(x, y);
15715
15716   return TRUE;
15717 }
15718
15719 static boolean DropElement(struct PlayerInfo *player)
15720 {
15721   int old_element, new_element;
15722   int dropx = player->jx, dropy = player->jy;
15723   int drop_direction = player->MovDir;
15724   int drop_side = drop_direction;
15725 #if 1
15726   int drop_element = get_next_dropped_element(player);
15727 #else
15728   int drop_element = (player->inventory_size > 0 ?
15729                       player->inventory_element[player->inventory_size - 1] :
15730                       player->inventory_infinite_element != EL_UNDEFINED ?
15731                       player->inventory_infinite_element :
15732                       player->dynabombs_left > 0 ?
15733                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15734                       EL_UNDEFINED);
15735 #endif
15736
15737   player->is_dropping_pressed = TRUE;
15738
15739   /* do not drop an element on top of another element; when holding drop key
15740      pressed without moving, dropped element must move away before the next
15741      element can be dropped (this is especially important if the next element
15742      is dynamite, which can be placed on background for historical reasons) */
15743   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15744     return MP_ACTION;
15745
15746   if (IS_THROWABLE(drop_element))
15747   {
15748     dropx += GET_DX_FROM_DIR(drop_direction);
15749     dropy += GET_DY_FROM_DIR(drop_direction);
15750
15751     if (!IN_LEV_FIELD(dropx, dropy))
15752       return FALSE;
15753   }
15754
15755   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15756   new_element = drop_element;           /* default: no change when dropping */
15757
15758   /* check if player is active, not moving and ready to drop */
15759   if (!player->active || player->MovPos || player->drop_delay > 0)
15760     return FALSE;
15761
15762   /* check if player has anything that can be dropped */
15763   if (new_element == EL_UNDEFINED)
15764     return FALSE;
15765
15766   /* check if drop key was pressed long enough for EM style dynamite */
15767   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15768     return FALSE;
15769
15770   /* check if anything can be dropped at the current position */
15771   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15772     return FALSE;
15773
15774   /* collected custom elements can only be dropped on empty fields */
15775   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15776     return FALSE;
15777
15778   if (old_element != EL_EMPTY)
15779     Back[dropx][dropy] = old_element;   /* store old element on this field */
15780
15781   ResetGfxAnimation(dropx, dropy);
15782   ResetRandomAnimationValue(dropx, dropy);
15783
15784   if (player->inventory_size > 0 ||
15785       player->inventory_infinite_element != EL_UNDEFINED)
15786   {
15787     if (player->inventory_size > 0)
15788     {
15789       player->inventory_size--;
15790
15791       DrawGameDoorValues();
15792
15793       if (new_element == EL_DYNAMITE)
15794         new_element = EL_DYNAMITE_ACTIVE;
15795       else if (new_element == EL_EM_DYNAMITE)
15796         new_element = EL_EM_DYNAMITE_ACTIVE;
15797       else if (new_element == EL_SP_DISK_RED)
15798         new_element = EL_SP_DISK_RED_ACTIVE;
15799     }
15800
15801     Feld[dropx][dropy] = new_element;
15802
15803     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15804       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15805                           el2img(Feld[dropx][dropy]), 0);
15806
15807     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15808
15809     /* needed if previous element just changed to "empty" in the last frame */
15810     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15811
15812     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15813                                player->index_bit, drop_side);
15814     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15815                                         CE_PLAYER_DROPS_X,
15816                                         player->index_bit, drop_side);
15817
15818     TestIfElementTouchesCustomElement(dropx, dropy);
15819   }
15820   else          /* player is dropping a dyna bomb */
15821   {
15822     player->dynabombs_left--;
15823
15824     Feld[dropx][dropy] = new_element;
15825
15826     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15827       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15828                           el2img(Feld[dropx][dropy]), 0);
15829
15830     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15831   }
15832
15833   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15834     InitField_WithBug1(dropx, dropy, FALSE);
15835
15836   new_element = Feld[dropx][dropy];     /* element might have changed */
15837
15838   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15839       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15840   {
15841     int move_direction, nextx, nexty;
15842
15843     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15844       MovDir[dropx][dropy] = drop_direction;
15845
15846     move_direction = MovDir[dropx][dropy];
15847     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15848     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15849
15850     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15851
15852 #if USE_FIX_IMPACT_COLLISION
15853     /* do not cause impact style collision by dropping elements that can fall */
15854     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15855 #else
15856     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15857 #endif
15858   }
15859
15860   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15861   player->is_dropping = TRUE;
15862
15863   player->drop_pressed_delay = 0;
15864   player->is_dropping_pressed = FALSE;
15865
15866   player->drop_x = dropx;
15867   player->drop_y = dropy;
15868
15869   return TRUE;
15870 }
15871
15872 /* ------------------------------------------------------------------------- */
15873 /* game sound playing functions                                              */
15874 /* ------------------------------------------------------------------------- */
15875
15876 static int *loop_sound_frame = NULL;
15877 static int *loop_sound_volume = NULL;
15878
15879 void InitPlayLevelSound()
15880 {
15881   int num_sounds = getSoundListSize();
15882
15883   checked_free(loop_sound_frame);
15884   checked_free(loop_sound_volume);
15885
15886   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15887   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15888 }
15889
15890 static void PlayLevelSound(int x, int y, int nr)
15891 {
15892   int sx = SCREENX(x), sy = SCREENY(y);
15893   int volume, stereo_position;
15894   int max_distance = 8;
15895   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15896
15897   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15898       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15899     return;
15900
15901   if (!IN_LEV_FIELD(x, y) ||
15902       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15903       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15904     return;
15905
15906   volume = SOUND_MAX_VOLUME;
15907
15908   if (!IN_SCR_FIELD(sx, sy))
15909   {
15910     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15911     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15912
15913     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15914   }
15915
15916   stereo_position = (SOUND_MAX_LEFT +
15917                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15918                      (SCR_FIELDX + 2 * max_distance));
15919
15920   if (IS_LOOP_SOUND(nr))
15921   {
15922     /* This assures that quieter loop sounds do not overwrite louder ones,
15923        while restarting sound volume comparison with each new game frame. */
15924
15925     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15926       return;
15927
15928     loop_sound_volume[nr] = volume;
15929     loop_sound_frame[nr] = FrameCounter;
15930   }
15931
15932   PlaySoundExt(nr, volume, stereo_position, type);
15933 }
15934
15935 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15936 {
15937   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15938                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15939                  y < LEVELY(BY1) ? LEVELY(BY1) :
15940                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15941                  sound_action);
15942 }
15943
15944 static void PlayLevelSoundAction(int x, int y, int action)
15945 {
15946   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15947 }
15948
15949 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15950 {
15951   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15952
15953   if (sound_effect != SND_UNDEFINED)
15954     PlayLevelSound(x, y, sound_effect);
15955 }
15956
15957 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15958                                               int action)
15959 {
15960   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15961
15962   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15963     PlayLevelSound(x, y, sound_effect);
15964 }
15965
15966 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15967 {
15968   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15969
15970   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15971     PlayLevelSound(x, y, sound_effect);
15972 }
15973
15974 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15975 {
15976   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15977
15978   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15979     StopSound(sound_effect);
15980 }
15981
15982 static void PlayLevelMusic()
15983 {
15984   if (levelset.music[level_nr] != MUS_UNDEFINED)
15985     PlayMusic(levelset.music[level_nr]);        /* from config file */
15986   else
15987     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15988 }
15989
15990 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15991 {
15992   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15993   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15994   int x = xx - 1 - offset;
15995   int y = yy - 1 - offset;
15996
15997   switch (sample)
15998   {
15999     case SAMPLE_blank:
16000       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16001       break;
16002
16003     case SAMPLE_roll:
16004       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16005       break;
16006
16007     case SAMPLE_stone:
16008       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16009       break;
16010
16011     case SAMPLE_nut:
16012       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16013       break;
16014
16015     case SAMPLE_crack:
16016       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16017       break;
16018
16019     case SAMPLE_bug:
16020       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16021       break;
16022
16023     case SAMPLE_tank:
16024       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16025       break;
16026
16027     case SAMPLE_android_clone:
16028       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16029       break;
16030
16031     case SAMPLE_android_move:
16032       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16033       break;
16034
16035     case SAMPLE_spring:
16036       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16037       break;
16038
16039     case SAMPLE_slurp:
16040       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16041       break;
16042
16043     case SAMPLE_eater:
16044       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16045       break;
16046
16047     case SAMPLE_eater_eat:
16048       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16049       break;
16050
16051     case SAMPLE_alien:
16052       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16053       break;
16054
16055     case SAMPLE_collect:
16056       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16057       break;
16058
16059     case SAMPLE_diamond:
16060       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16061       break;
16062
16063     case SAMPLE_squash:
16064       /* !!! CHECK THIS !!! */
16065 #if 1
16066       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16067 #else
16068       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16069 #endif
16070       break;
16071
16072     case SAMPLE_wonderfall:
16073       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16074       break;
16075
16076     case SAMPLE_drip:
16077       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16078       break;
16079
16080     case SAMPLE_push:
16081       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16082       break;
16083
16084     case SAMPLE_dirt:
16085       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16086       break;
16087
16088     case SAMPLE_acid:
16089       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16090       break;
16091
16092     case SAMPLE_ball:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16094       break;
16095
16096     case SAMPLE_grow:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16098       break;
16099
16100     case SAMPLE_wonder:
16101       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16102       break;
16103
16104     case SAMPLE_door:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16106       break;
16107
16108     case SAMPLE_exit_open:
16109       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16110       break;
16111
16112     case SAMPLE_exit_leave:
16113       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16114       break;
16115
16116     case SAMPLE_dynamite:
16117       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16118       break;
16119
16120     case SAMPLE_tick:
16121       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16122       break;
16123
16124     case SAMPLE_press:
16125       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16126       break;
16127
16128     case SAMPLE_wheel:
16129       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16130       break;
16131
16132     case SAMPLE_boom:
16133       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16134       break;
16135
16136     case SAMPLE_die:
16137       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16138       break;
16139
16140     case SAMPLE_time:
16141       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16142       break;
16143
16144     default:
16145       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16146       break;
16147   }
16148 }
16149
16150 #if 0
16151 void ChangeTime(int value)
16152 {
16153   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16154
16155   *time += value;
16156
16157   /* EMC game engine uses value from time counter of RND game engine */
16158   level.native_em_level->lev->time = *time;
16159
16160   DrawGameValue_Time(*time);
16161 }
16162
16163 void RaiseScore(int value)
16164 {
16165   /* EMC game engine and RND game engine have separate score counters */
16166   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16167                 &level.native_em_level->lev->score : &local_player->score);
16168
16169   *score += value;
16170
16171   DrawGameValue_Score(*score);
16172 }
16173 #endif
16174
16175 void RaiseScore(int value)
16176 {
16177   local_player->score += value;
16178
16179 #if 1
16180   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16181
16182   DisplayGameControlValues();
16183 #else
16184   DrawGameValue_Score(local_player->score);
16185 #endif
16186 }
16187
16188 void RaiseScoreElement(int element)
16189 {
16190   switch (element)
16191   {
16192     case EL_EMERALD:
16193     case EL_BD_DIAMOND:
16194     case EL_EMERALD_YELLOW:
16195     case EL_EMERALD_RED:
16196     case EL_EMERALD_PURPLE:
16197     case EL_SP_INFOTRON:
16198       RaiseScore(level.score[SC_EMERALD]);
16199       break;
16200     case EL_DIAMOND:
16201       RaiseScore(level.score[SC_DIAMOND]);
16202       break;
16203     case EL_CRYSTAL:
16204       RaiseScore(level.score[SC_CRYSTAL]);
16205       break;
16206     case EL_PEARL:
16207       RaiseScore(level.score[SC_PEARL]);
16208       break;
16209     case EL_BUG:
16210     case EL_BD_BUTTERFLY:
16211     case EL_SP_ELECTRON:
16212       RaiseScore(level.score[SC_BUG]);
16213       break;
16214     case EL_SPACESHIP:
16215     case EL_BD_FIREFLY:
16216     case EL_SP_SNIKSNAK:
16217       RaiseScore(level.score[SC_SPACESHIP]);
16218       break;
16219     case EL_YAMYAM:
16220     case EL_DARK_YAMYAM:
16221       RaiseScore(level.score[SC_YAMYAM]);
16222       break;
16223     case EL_ROBOT:
16224       RaiseScore(level.score[SC_ROBOT]);
16225       break;
16226     case EL_PACMAN:
16227       RaiseScore(level.score[SC_PACMAN]);
16228       break;
16229     case EL_NUT:
16230       RaiseScore(level.score[SC_NUT]);
16231       break;
16232     case EL_DYNAMITE:
16233     case EL_EM_DYNAMITE:
16234     case EL_SP_DISK_RED:
16235     case EL_DYNABOMB_INCREASE_NUMBER:
16236     case EL_DYNABOMB_INCREASE_SIZE:
16237     case EL_DYNABOMB_INCREASE_POWER:
16238       RaiseScore(level.score[SC_DYNAMITE]);
16239       break;
16240     case EL_SHIELD_NORMAL:
16241     case EL_SHIELD_DEADLY:
16242       RaiseScore(level.score[SC_SHIELD]);
16243       break;
16244     case EL_EXTRA_TIME:
16245       RaiseScore(level.extra_time_score);
16246       break;
16247     case EL_KEY_1:
16248     case EL_KEY_2:
16249     case EL_KEY_3:
16250     case EL_KEY_4:
16251     case EL_EM_KEY_1:
16252     case EL_EM_KEY_2:
16253     case EL_EM_KEY_3:
16254     case EL_EM_KEY_4:
16255     case EL_EMC_KEY_5:
16256     case EL_EMC_KEY_6:
16257     case EL_EMC_KEY_7:
16258     case EL_EMC_KEY_8:
16259     case EL_DC_KEY_WHITE:
16260       RaiseScore(level.score[SC_KEY]);
16261       break;
16262     default:
16263       RaiseScore(element_info[element].collect_score);
16264       break;
16265   }
16266 }
16267
16268 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16269 {
16270   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16271   {
16272 #if defined(NETWORK_AVALIABLE)
16273     if (options.network)
16274       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16275     else
16276 #endif
16277     {
16278       if (quick_quit)
16279       {
16280 #if 1
16281
16282 #if 1
16283         FadeSkipNextFadeIn();
16284 #else
16285         fading = fading_none;
16286 #endif
16287
16288 #else
16289         OpenDoor(DOOR_CLOSE_1);
16290 #endif
16291
16292         game_status = GAME_MODE_MAIN;
16293
16294 #if 1
16295         DrawAndFadeInMainMenu(REDRAW_FIELD);
16296 #else
16297         DrawMainMenu();
16298 #endif
16299       }
16300       else
16301       {
16302 #if 0
16303         FadeOut(REDRAW_FIELD);
16304 #endif
16305
16306         game_status = GAME_MODE_MAIN;
16307
16308         DrawAndFadeInMainMenu(REDRAW_FIELD);
16309       }
16310     }
16311   }
16312   else          /* continue playing the game */
16313   {
16314     if (tape.playing && tape.deactivate_display)
16315       TapeDeactivateDisplayOff(TRUE);
16316
16317     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16318
16319     if (tape.playing && tape.deactivate_display)
16320       TapeDeactivateDisplayOn();
16321   }
16322 }
16323
16324 void RequestQuitGame(boolean ask_if_really_quit)
16325 {
16326   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16327   boolean skip_request = AllPlayersGone || quick_quit;
16328
16329   RequestQuitGameExt(skip_request, quick_quit,
16330                      "Do you really want to quit the game ?");
16331 }
16332
16333
16334 /* ------------------------------------------------------------------------- */
16335 /* random generator functions                                                */
16336 /* ------------------------------------------------------------------------- */
16337
16338 unsigned int InitEngineRandom_RND(long seed)
16339 {
16340   game.num_random_calls = 0;
16341
16342 #if 0
16343   unsigned int rnd_seed = InitEngineRandom(seed);
16344
16345   printf("::: START RND: %d\n", rnd_seed);
16346
16347   return rnd_seed;
16348 #else
16349
16350   return InitEngineRandom(seed);
16351
16352 #endif
16353
16354 }
16355
16356 unsigned int RND(int max)
16357 {
16358   if (max > 0)
16359   {
16360     game.num_random_calls++;
16361
16362     return GetEngineRandom(max);
16363   }
16364
16365   return 0;
16366 }
16367
16368
16369 /* ------------------------------------------------------------------------- */
16370 /* game engine snapshot handling functions                                   */
16371 /* ------------------------------------------------------------------------- */
16372
16373 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16374
16375 struct EngineSnapshotInfo
16376 {
16377   /* runtime values for custom element collect score */
16378   int collect_score[NUM_CUSTOM_ELEMENTS];
16379
16380   /* runtime values for group element choice position */
16381   int choice_pos[NUM_GROUP_ELEMENTS];
16382
16383   /* runtime values for belt position animations */
16384   int belt_graphic[4 * NUM_BELT_PARTS];
16385   int belt_anim_mode[4 * NUM_BELT_PARTS];
16386 };
16387
16388 struct EngineSnapshotNodeInfo
16389 {
16390   void *buffer_orig;
16391   void *buffer_copy;
16392   int size;
16393 };
16394
16395 static struct EngineSnapshotInfo engine_snapshot_rnd;
16396 static ListNode *engine_snapshot_list = NULL;
16397 static char *snapshot_level_identifier = NULL;
16398 static int snapshot_level_nr = -1;
16399
16400 void FreeEngineSnapshot()
16401 {
16402   while (engine_snapshot_list != NULL)
16403     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16404                        checked_free);
16405
16406   setString(&snapshot_level_identifier, NULL);
16407   snapshot_level_nr = -1;
16408 }
16409
16410 static void SaveEngineSnapshotValues_RND()
16411 {
16412   static int belt_base_active_element[4] =
16413   {
16414     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16415     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16416     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16417     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16418   };
16419   int i, j;
16420
16421   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16422   {
16423     int element = EL_CUSTOM_START + i;
16424
16425     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16426   }
16427
16428   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16429   {
16430     int element = EL_GROUP_START + i;
16431
16432     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16433   }
16434
16435   for (i = 0; i < 4; i++)
16436   {
16437     for (j = 0; j < NUM_BELT_PARTS; j++)
16438     {
16439       int element = belt_base_active_element[i] + j;
16440       int graphic = el2img(element);
16441       int anim_mode = graphic_info[graphic].anim_mode;
16442
16443       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16444       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16445     }
16446   }
16447 }
16448
16449 static void LoadEngineSnapshotValues_RND()
16450 {
16451   unsigned long num_random_calls = game.num_random_calls;
16452   int i, j;
16453
16454   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16455   {
16456     int element = EL_CUSTOM_START + i;
16457
16458     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16459   }
16460
16461   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16462   {
16463     int element = EL_GROUP_START + i;
16464
16465     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16466   }
16467
16468   for (i = 0; i < 4; i++)
16469   {
16470     for (j = 0; j < NUM_BELT_PARTS; j++)
16471     {
16472       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16473       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16474
16475       graphic_info[graphic].anim_mode = anim_mode;
16476     }
16477   }
16478
16479   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16480   {
16481     InitRND(tape.random_seed);
16482     for (i = 0; i < num_random_calls; i++)
16483       RND(1);
16484   }
16485
16486   if (game.num_random_calls != num_random_calls)
16487   {
16488     Error(ERR_INFO, "number of random calls out of sync");
16489     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16490     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16491     Error(ERR_EXIT, "this should not happen -- please debug");
16492   }
16493 }
16494
16495 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16496 {
16497   struct EngineSnapshotNodeInfo *bi =
16498     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16499
16500   bi->buffer_orig = buffer;
16501   bi->buffer_copy = checked_malloc(size);
16502   bi->size = size;
16503
16504   memcpy(bi->buffer_copy, buffer, size);
16505
16506   addNodeToList(&engine_snapshot_list, NULL, bi);
16507 }
16508
16509 void SaveEngineSnapshot()
16510 {
16511   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16512
16513   if (level_editor_test_game)   /* do not save snapshots from editor */
16514     return;
16515
16516   /* copy some special values to a structure better suited for the snapshot */
16517
16518   SaveEngineSnapshotValues_RND();
16519   SaveEngineSnapshotValues_EM();
16520
16521   /* save values stored in special snapshot structure */
16522
16523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16525
16526   /* save further RND engine values */
16527
16528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16531
16532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16536
16537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16542
16543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16546
16547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16548
16549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16550
16551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16553
16554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16572
16573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16575
16576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16579
16580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16582
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16588
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16591
16592   /* save level identification information */
16593
16594   setString(&snapshot_level_identifier, leveldir_current->identifier);
16595   snapshot_level_nr = level_nr;
16596
16597 #if 0
16598   ListNode *node = engine_snapshot_list;
16599   int num_bytes = 0;
16600
16601   while (node != NULL)
16602   {
16603     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16604
16605     node = node->next;
16606   }
16607
16608   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16609 #endif
16610 }
16611
16612 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16613 {
16614   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16615 }
16616
16617 void LoadEngineSnapshot()
16618 {
16619   ListNode *node = engine_snapshot_list;
16620
16621   if (engine_snapshot_list == NULL)
16622     return;
16623
16624   while (node != NULL)
16625   {
16626     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16627
16628     node = node->next;
16629   }
16630
16631   /* restore special values from snapshot structure */
16632
16633   LoadEngineSnapshotValues_RND();
16634   LoadEngineSnapshotValues_EM();
16635 }
16636
16637 boolean CheckEngineSnapshot()
16638 {
16639   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16640           snapshot_level_nr == level_nr);
16641 }
16642
16643
16644 /* ---------- new game button stuff ---------------------------------------- */
16645
16646 /* graphic position values for game buttons */
16647 #define GAME_BUTTON_XSIZE       30
16648 #define GAME_BUTTON_YSIZE       30
16649 #define GAME_BUTTON_XPOS        5
16650 #define GAME_BUTTON_YPOS        215
16651 #define SOUND_BUTTON_XPOS       5
16652 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16653
16654 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16655 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16656 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16657 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16658 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16659 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16660
16661 static struct
16662 {
16663   int *x, *y;
16664   int gd_x, gd_y;
16665   int gadget_id;
16666   char *infotext;
16667 } gamebutton_info[NUM_GAME_BUTTONS] =
16668 {
16669 #if 1
16670   {
16671     &game.button.stop.x,        &game.button.stop.y,
16672     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16673     GAME_CTRL_ID_STOP,
16674     "stop game"
16675   },
16676   {
16677     &game.button.pause.x,       &game.button.pause.y,
16678     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16679     GAME_CTRL_ID_PAUSE,
16680     "pause game"
16681   },
16682   {
16683     &game.button.play.x,        &game.button.play.y,
16684     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16685     GAME_CTRL_ID_PLAY,
16686     "play game"
16687   },
16688   {
16689     &game.button.sound_music.x, &game.button.sound_music.y,
16690     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16691     SOUND_CTRL_ID_MUSIC,
16692     "background music on/off"
16693   },
16694   {
16695     &game.button.sound_loops.x, &game.button.sound_loops.y,
16696     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16697     SOUND_CTRL_ID_LOOPS,
16698     "sound loops on/off"
16699   },
16700   {
16701     &game.button.sound_simple.x,&game.button.sound_simple.y,
16702     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16703     SOUND_CTRL_ID_SIMPLE,
16704     "normal sounds on/off"
16705   }
16706 #else
16707   {
16708     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16709     GAME_CTRL_ID_STOP,
16710     "stop game"
16711   },
16712   {
16713     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16714     GAME_CTRL_ID_PAUSE,
16715     "pause game"
16716   },
16717   {
16718     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16719     GAME_CTRL_ID_PLAY,
16720     "play game"
16721   },
16722   {
16723     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16724     SOUND_CTRL_ID_MUSIC,
16725     "background music on/off"
16726   },
16727   {
16728     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16729     SOUND_CTRL_ID_LOOPS,
16730     "sound loops on/off"
16731   },
16732   {
16733     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16734     SOUND_CTRL_ID_SIMPLE,
16735     "normal sounds on/off"
16736   }
16737 #endif
16738 };
16739
16740 void CreateGameButtons()
16741 {
16742   int i;
16743
16744   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16745   {
16746     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16747     struct GadgetInfo *gi;
16748     int button_type;
16749     boolean checked;
16750     unsigned long event_mask;
16751     int x, y;
16752     int gd_xoffset, gd_yoffset;
16753     int gd_x1, gd_x2, gd_y1, gd_y2;
16754     int id = i;
16755
16756     x = DX + *gamebutton_info[i].x;
16757     y = DY + *gamebutton_info[i].y;
16758     gd_xoffset = gamebutton_info[i].gd_x;
16759     gd_yoffset = gamebutton_info[i].gd_y;
16760     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16761     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16762
16763     if (id == GAME_CTRL_ID_STOP ||
16764         id == GAME_CTRL_ID_PAUSE ||
16765         id == GAME_CTRL_ID_PLAY)
16766     {
16767       button_type = GD_TYPE_NORMAL_BUTTON;
16768       checked = FALSE;
16769       event_mask = GD_EVENT_RELEASED;
16770       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16771       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16772     }
16773     else
16774     {
16775       button_type = GD_TYPE_CHECK_BUTTON;
16776       checked =
16777         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16778          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16779          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16780       event_mask = GD_EVENT_PRESSED;
16781       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16782       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16783     }
16784
16785     gi = CreateGadget(GDI_CUSTOM_ID, id,
16786                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16787 #if 1
16788                       GDI_X, x,
16789                       GDI_Y, y,
16790 #else
16791                       GDI_X, DX + gd_xoffset,
16792                       GDI_Y, DY + gd_yoffset,
16793 #endif
16794                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16795                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16796                       GDI_TYPE, button_type,
16797                       GDI_STATE, GD_BUTTON_UNPRESSED,
16798                       GDI_CHECKED, checked,
16799                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16800                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16801                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16802                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16803                       GDI_DIRECT_DRAW, FALSE,
16804                       GDI_EVENT_MASK, event_mask,
16805                       GDI_CALLBACK_ACTION, HandleGameButtons,
16806                       GDI_END);
16807
16808     if (gi == NULL)
16809       Error(ERR_EXIT, "cannot create gadget");
16810
16811     game_gadget[id] = gi;
16812   }
16813 }
16814
16815 void FreeGameButtons()
16816 {
16817   int i;
16818
16819   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16820     FreeGadget(game_gadget[i]);
16821 }
16822
16823 static void MapGameButtons()
16824 {
16825   int i;
16826
16827   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16828     MapGadget(game_gadget[i]);
16829 }
16830
16831 void UnmapGameButtons()
16832 {
16833   int i;
16834
16835   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16836     UnmapGadget(game_gadget[i]);
16837 }
16838
16839 void RedrawGameButtons()
16840 {
16841   int i;
16842
16843   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16844     RedrawGadget(game_gadget[i]);
16845 }
16846
16847 static void HandleGameButtons(struct GadgetInfo *gi)
16848 {
16849   int id = gi->custom_id;
16850
16851   if (game_status != GAME_MODE_PLAYING)
16852     return;
16853
16854   switch (id)
16855   {
16856     case GAME_CTRL_ID_STOP:
16857       if (tape.playing)
16858         TapeStop();
16859       else
16860         RequestQuitGame(TRUE);
16861       break;
16862
16863     case GAME_CTRL_ID_PAUSE:
16864       if (options.network)
16865       {
16866 #if defined(NETWORK_AVALIABLE)
16867         if (tape.pausing)
16868           SendToServer_ContinuePlaying();
16869         else
16870           SendToServer_PausePlaying();
16871 #endif
16872       }
16873       else
16874         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16875       break;
16876
16877     case GAME_CTRL_ID_PLAY:
16878       if (tape.pausing)
16879       {
16880 #if defined(NETWORK_AVALIABLE)
16881         if (options.network)
16882           SendToServer_ContinuePlaying();
16883         else
16884 #endif
16885         {
16886           tape.pausing = FALSE;
16887           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16888         }
16889       }
16890       break;
16891
16892     case SOUND_CTRL_ID_MUSIC:
16893       if (setup.sound_music)
16894       { 
16895         setup.sound_music = FALSE;
16896         FadeMusic();
16897       }
16898       else if (audio.music_available)
16899       { 
16900         setup.sound = setup.sound_music = TRUE;
16901
16902         SetAudioMode(setup.sound);
16903
16904         PlayLevelMusic();
16905       }
16906       break;
16907
16908     case SOUND_CTRL_ID_LOOPS:
16909       if (setup.sound_loops)
16910         setup.sound_loops = FALSE;
16911       else if (audio.loops_available)
16912       {
16913         setup.sound = setup.sound_loops = TRUE;
16914         SetAudioMode(setup.sound);
16915       }
16916       break;
16917
16918     case SOUND_CTRL_ID_SIMPLE:
16919       if (setup.sound_simple)
16920         setup.sound_simple = FALSE;
16921       else if (audio.sound_available)
16922       {
16923         setup.sound = setup.sound_simple = TRUE;
16924         SetAudioMode(setup.sound);
16925       }
16926       break;
16927
16928     default:
16929       break;
16930   }
16931 }