rnd-20080115-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
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 1)
65
66 #if USE_DELAYED_GFX_REDRAW
67 #define TEST_DrawLevelField(x, y)                               \
68         GfxRedraw[x][y] |= GFX_REDRAW_TILE
69 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
70         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
71 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
72         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
73 #define TEST_DrawTwinkleOnField(x, y)                           \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
75 #else
76 #define TEST_DrawLevelField(x, y)                               \
77              DrawLevelField(x, y)
78 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
79              DrawLevelFieldCrumbledSand(x, y)
80 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
81              DrawLevelFieldCrumbledSandNeighbours(x, y)
82 #define TEST_DrawTwinkleOnField(x, y)                           \
83              DrawTwinkleOnField(x, y)
84 #endif
85
86
87 /* for DigField() */
88 #define DF_NO_PUSH              0
89 #define DF_DIG                  1
90 #define DF_SNAP                 2
91
92 /* for MovePlayer() */
93 #define MP_NO_ACTION            0
94 #define MP_MOVING               1
95 #define MP_ACTION               2
96 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
97
98 /* for ScrollPlayer() */
99 #define SCROLL_INIT             0
100 #define SCROLL_GO_ON            1
101
102 /* for Bang()/Explode() */
103 #define EX_PHASE_START          0
104 #define EX_TYPE_NONE            0
105 #define EX_TYPE_NORMAL          (1 << 0)
106 #define EX_TYPE_CENTER          (1 << 1)
107 #define EX_TYPE_BORDER          (1 << 2)
108 #define EX_TYPE_CROSS           (1 << 3)
109 #define EX_TYPE_DYNA            (1 << 4)
110 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
111
112 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
113 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
114 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
115 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
116
117 /* special positions in the game control window (relative to control window) */
118 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
119 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
120 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
121 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
122 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
123 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
124 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
125 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
126 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
127 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
128 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
129 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
130 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
131 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
132 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
133 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
134
135 /* special positions in the game control window (relative to main window) */
136 #define DX_LEVEL1               (DX + XX_LEVEL1)
137 #define DX_LEVEL2               (DX + XX_LEVEL2)
138 #define DX_LEVEL                (DX + XX_LEVEL)
139 #define DY_LEVEL                (DY + YY_LEVEL)
140 #define DX_EMERALDS             (DX + XX_EMERALDS)
141 #define DY_EMERALDS             (DY + YY_EMERALDS)
142 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
143 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
144 #define DX_KEYS                 (DX + XX_KEYS)
145 #define DY_KEYS                 (DY + YY_KEYS)
146 #define DX_SCORE                (DX + XX_SCORE)
147 #define DY_SCORE                (DY + YY_SCORE)
148 #define DX_TIME1                (DX + XX_TIME1)
149 #define DX_TIME2                (DX + XX_TIME2)
150 #define DX_TIME                 (DX + XX_TIME)
151 #define DY_TIME                 (DY + YY_TIME)
152
153 #if 1
154 /* game panel display and control definitions */
155
156 #define GAME_PANEL_LEVEL_NUMBER                 0
157 #define GAME_PANEL_GEMS                         1
158 #define GAME_PANEL_INVENTORY_COUNT              2
159 #define GAME_PANEL_INVENTORY_FIRST_1            3
160 #define GAME_PANEL_INVENTORY_FIRST_2            4
161 #define GAME_PANEL_INVENTORY_FIRST_3            5
162 #define GAME_PANEL_INVENTORY_FIRST_4            6
163 #define GAME_PANEL_INVENTORY_FIRST_5            7
164 #define GAME_PANEL_INVENTORY_FIRST_6            8
165 #define GAME_PANEL_INVENTORY_FIRST_7            9
166 #define GAME_PANEL_INVENTORY_FIRST_8            10
167 #define GAME_PANEL_INVENTORY_LAST_1             11
168 #define GAME_PANEL_INVENTORY_LAST_2             12
169 #define GAME_PANEL_INVENTORY_LAST_3             13
170 #define GAME_PANEL_INVENTORY_LAST_4             14
171 #define GAME_PANEL_INVENTORY_LAST_5             15
172 #define GAME_PANEL_INVENTORY_LAST_6             16
173 #define GAME_PANEL_INVENTORY_LAST_7             17
174 #define GAME_PANEL_INVENTORY_LAST_8             18
175 #define GAME_PANEL_KEY_1                        19
176 #define GAME_PANEL_KEY_2                        20
177 #define GAME_PANEL_KEY_3                        21
178 #define GAME_PANEL_KEY_4                        22
179 #define GAME_PANEL_KEY_5                        23
180 #define GAME_PANEL_KEY_6                        24
181 #define GAME_PANEL_KEY_7                        25
182 #define GAME_PANEL_KEY_8                        26
183 #define GAME_PANEL_KEY_WHITE                    27
184 #define GAME_PANEL_KEY_WHITE_COUNT              28
185 #define GAME_PANEL_SCORE                        29
186 #define GAME_PANEL_HIGHSCORE                    30
187 #define GAME_PANEL_TIME                         31
188 #define GAME_PANEL_TIME_HH                      32
189 #define GAME_PANEL_TIME_MM                      33
190 #define GAME_PANEL_TIME_SS                      34
191 #define GAME_PANEL_SHIELD_NORMAL                35
192 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
193 #define GAME_PANEL_SHIELD_DEADLY                37
194 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
195 #define GAME_PANEL_EXIT                         39
196 #define GAME_PANEL_EMC_MAGIC_BALL               40
197 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
198 #define GAME_PANEL_LIGHT_SWITCH                 42
199 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
200 #define GAME_PANEL_TIMEGATE_SWITCH              44
201 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
202 #define GAME_PANEL_SWITCHGATE_SWITCH            46
203 #define GAME_PANEL_EMC_LENSES                   47
204 #define GAME_PANEL_EMC_LENSES_TIME              48
205 #define GAME_PANEL_EMC_MAGNIFIER                49
206 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
207 #define GAME_PANEL_BALLOON_SWITCH               51
208 #define GAME_PANEL_DYNABOMB_NUMBER              52
209 #define GAME_PANEL_DYNABOMB_SIZE                53
210 #define GAME_PANEL_DYNABOMB_POWER               54
211 #define GAME_PANEL_PENGUINS                     55
212 #define GAME_PANEL_SOKOBAN_OBJECTS              56
213 #define GAME_PANEL_SOKOBAN_FIELDS               57
214 #define GAME_PANEL_ROBOT_WHEEL                  58
215 #define GAME_PANEL_CONVEYOR_BELT_1              59
216 #define GAME_PANEL_CONVEYOR_BELT_2              60
217 #define GAME_PANEL_CONVEYOR_BELT_3              61
218 #define GAME_PANEL_CONVEYOR_BELT_4              62
219 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
220 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
221 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
222 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
223 #define GAME_PANEL_MAGIC_WALL                   67
224 #define GAME_PANEL_MAGIC_WALL_TIME              68
225 #define GAME_PANEL_GRAVITY_STATE                69
226 #define GAME_PANEL_GRAPHIC_1                    70
227 #define GAME_PANEL_GRAPHIC_2                    71
228 #define GAME_PANEL_GRAPHIC_3                    72
229 #define GAME_PANEL_GRAPHIC_4                    73
230 #define GAME_PANEL_GRAPHIC_5                    74
231 #define GAME_PANEL_GRAPHIC_6                    75
232 #define GAME_PANEL_GRAPHIC_7                    76
233 #define GAME_PANEL_GRAPHIC_8                    77
234 #define GAME_PANEL_ELEMENT_1                    78
235 #define GAME_PANEL_ELEMENT_2                    79
236 #define GAME_PANEL_ELEMENT_3                    80
237 #define GAME_PANEL_ELEMENT_4                    81
238 #define GAME_PANEL_ELEMENT_5                    82
239 #define GAME_PANEL_ELEMENT_6                    83
240 #define GAME_PANEL_ELEMENT_7                    84
241 #define GAME_PANEL_ELEMENT_8                    85
242 #define GAME_PANEL_ELEMENT_COUNT_1              86
243 #define GAME_PANEL_ELEMENT_COUNT_2              87
244 #define GAME_PANEL_ELEMENT_COUNT_3              88
245 #define GAME_PANEL_ELEMENT_COUNT_4              89
246 #define GAME_PANEL_ELEMENT_COUNT_5              90
247 #define GAME_PANEL_ELEMENT_COUNT_6              91
248 #define GAME_PANEL_ELEMENT_COUNT_7              92
249 #define GAME_PANEL_ELEMENT_COUNT_8              93
250 #define GAME_PANEL_CE_SCORE_1                   94
251 #define GAME_PANEL_CE_SCORE_2                   95
252 #define GAME_PANEL_CE_SCORE_3                   96
253 #define GAME_PANEL_CE_SCORE_4                   97
254 #define GAME_PANEL_CE_SCORE_5                   98
255 #define GAME_PANEL_CE_SCORE_6                   99
256 #define GAME_PANEL_CE_SCORE_7                   100
257 #define GAME_PANEL_CE_SCORE_8                   101
258 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
259 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
260 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
261 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
262 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
263 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
264 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
265 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
266 #define GAME_PANEL_PLAYER_NAME                  110
267 #define GAME_PANEL_LEVEL_NAME                   111
268 #define GAME_PANEL_LEVEL_AUTHOR                 112
269
270 #define NUM_GAME_PANEL_CONTROLS                 113
271
272 struct GamePanelOrderInfo
273 {
274   int nr;
275   int sort_priority;
276 };
277
278 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
279
280 struct GamePanelControlInfo
281 {
282   int nr;
283
284   struct TextPosInfo *pos;
285   int type;
286
287   int value, last_value;
288   int frame, last_frame;
289   int gfx_frame;
290   int gfx_random;
291 };
292
293 static struct GamePanelControlInfo game_panel_controls[] =
294 {
295   {
296     GAME_PANEL_LEVEL_NUMBER,
297     &game.panel.level_number,
298     TYPE_INTEGER,
299   },
300   {
301     GAME_PANEL_GEMS,
302     &game.panel.gems,
303     TYPE_INTEGER,
304   },
305   {
306     GAME_PANEL_INVENTORY_COUNT,
307     &game.panel.inventory_count,
308     TYPE_INTEGER,
309   },
310   {
311     GAME_PANEL_INVENTORY_FIRST_1,
312     &game.panel.inventory_first[0],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_FIRST_2,
317     &game.panel.inventory_first[1],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_FIRST_3,
322     &game.panel.inventory_first[2],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_FIRST_4,
327     &game.panel.inventory_first[3],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_INVENTORY_FIRST_5,
332     &game.panel.inventory_first[4],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_INVENTORY_FIRST_6,
337     &game.panel.inventory_first[5],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_INVENTORY_FIRST_7,
342     &game.panel.inventory_first[6],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_INVENTORY_FIRST_8,
347     &game.panel.inventory_first[7],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_INVENTORY_LAST_1,
352     &game.panel.inventory_last[0],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_INVENTORY_LAST_2,
357     &game.panel.inventory_last[1],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_INVENTORY_LAST_3,
362     &game.panel.inventory_last[2],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_INVENTORY_LAST_4,
367     &game.panel.inventory_last[3],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_INVENTORY_LAST_5,
372     &game.panel.inventory_last[4],
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_INVENTORY_LAST_6,
377     &game.panel.inventory_last[5],
378     TYPE_ELEMENT,
379   },
380   {
381     GAME_PANEL_INVENTORY_LAST_7,
382     &game.panel.inventory_last[6],
383     TYPE_ELEMENT,
384   },
385   {
386     GAME_PANEL_INVENTORY_LAST_8,
387     &game.panel.inventory_last[7],
388     TYPE_ELEMENT,
389   },
390   {
391     GAME_PANEL_KEY_1,
392     &game.panel.key[0],
393     TYPE_ELEMENT,
394   },
395   {
396     GAME_PANEL_KEY_2,
397     &game.panel.key[1],
398     TYPE_ELEMENT,
399   },
400   {
401     GAME_PANEL_KEY_3,
402     &game.panel.key[2],
403     TYPE_ELEMENT,
404   },
405   {
406     GAME_PANEL_KEY_4,
407     &game.panel.key[3],
408     TYPE_ELEMENT,
409   },
410   {
411     GAME_PANEL_KEY_5,
412     &game.panel.key[4],
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_KEY_6,
417     &game.panel.key[5],
418     TYPE_ELEMENT,
419   },
420   {
421     GAME_PANEL_KEY_7,
422     &game.panel.key[6],
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_KEY_8,
427     &game.panel.key[7],
428     TYPE_ELEMENT,
429   },
430   {
431     GAME_PANEL_KEY_WHITE,
432     &game.panel.key_white,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_KEY_WHITE_COUNT,
437     &game.panel.key_white_count,
438     TYPE_INTEGER,
439   },
440   {
441     GAME_PANEL_SCORE,
442     &game.panel.score,
443     TYPE_INTEGER,
444   },
445   {
446     GAME_PANEL_HIGHSCORE,
447     &game.panel.highscore,
448     TYPE_INTEGER,
449   },
450   {
451     GAME_PANEL_TIME,
452     &game.panel.time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIME_HH,
457     &game.panel.time_hh,
458     TYPE_INTEGER,
459   },
460   {
461     GAME_PANEL_TIME_MM,
462     &game.panel.time_mm,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_TIME_SS,
467     &game.panel.time_ss,
468     TYPE_INTEGER,
469   },
470   {
471     GAME_PANEL_SHIELD_NORMAL,
472     &game.panel.shield_normal,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_SHIELD_NORMAL_TIME,
477     &game.panel.shield_normal_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_SHIELD_DEADLY,
482     &game.panel.shield_deadly,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_SHIELD_DEADLY_TIME,
487     &game.panel.shield_deadly_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_EXIT,
492     &game.panel.exit,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_EMC_MAGIC_BALL,
497     &game.panel.emc_magic_ball,
498     TYPE_ELEMENT,
499   },
500   {
501     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
502     &game.panel.emc_magic_ball_switch,
503     TYPE_ELEMENT,
504   },
505   {
506     GAME_PANEL_LIGHT_SWITCH,
507     &game.panel.light_switch,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_LIGHT_SWITCH_TIME,
512     &game.panel.light_switch_time,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_TIMEGATE_SWITCH,
517     &game.panel.timegate_switch,
518     TYPE_ELEMENT,
519   },
520   {
521     GAME_PANEL_TIMEGATE_SWITCH_TIME,
522     &game.panel.timegate_switch_time,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_SWITCHGATE_SWITCH,
527     &game.panel.switchgate_switch,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_EMC_LENSES,
532     &game.panel.emc_lenses,
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_EMC_LENSES_TIME,
537     &game.panel.emc_lenses_time,
538     TYPE_INTEGER,
539   },
540   {
541     GAME_PANEL_EMC_MAGNIFIER,
542     &game.panel.emc_magnifier,
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_EMC_MAGNIFIER_TIME,
547     &game.panel.emc_magnifier_time,
548     TYPE_INTEGER,
549   },
550   {
551     GAME_PANEL_BALLOON_SWITCH,
552     &game.panel.balloon_switch,
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_DYNABOMB_NUMBER,
557     &game.panel.dynabomb_number,
558     TYPE_INTEGER,
559   },
560   {
561     GAME_PANEL_DYNABOMB_SIZE,
562     &game.panel.dynabomb_size,
563     TYPE_INTEGER,
564   },
565   {
566     GAME_PANEL_DYNABOMB_POWER,
567     &game.panel.dynabomb_power,
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_PENGUINS,
572     &game.panel.penguins,
573     TYPE_INTEGER,
574   },
575   {
576     GAME_PANEL_SOKOBAN_OBJECTS,
577     &game.panel.sokoban_objects,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_SOKOBAN_FIELDS,
582     &game.panel.sokoban_fields,
583     TYPE_INTEGER,
584   },
585   {
586     GAME_PANEL_ROBOT_WHEEL,
587     &game.panel.robot_wheel,
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_CONVEYOR_BELT_1,
592     &game.panel.conveyor_belt[0],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_CONVEYOR_BELT_2,
597     &game.panel.conveyor_belt[1],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_CONVEYOR_BELT_3,
602     &game.panel.conveyor_belt[2],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_CONVEYOR_BELT_4,
607     &game.panel.conveyor_belt[3],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
612     &game.panel.conveyor_belt_switch[0],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
617     &game.panel.conveyor_belt_switch[1],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
622     &game.panel.conveyor_belt_switch[2],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
627     &game.panel.conveyor_belt_switch[3],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_MAGIC_WALL,
632     &game.panel.magic_wall,
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_MAGIC_WALL_TIME,
637     &game.panel.magic_wall_time,
638     TYPE_INTEGER,
639   },
640   {
641     GAME_PANEL_GRAVITY_STATE,
642     &game.panel.gravity_state,
643     TYPE_STRING,
644   },
645   {
646     GAME_PANEL_GRAPHIC_1,
647     &game.panel.graphic[0],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_GRAPHIC_2,
652     &game.panel.graphic[1],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_GRAPHIC_3,
657     &game.panel.graphic[2],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_GRAPHIC_4,
662     &game.panel.graphic[3],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_GRAPHIC_5,
667     &game.panel.graphic[4],
668     TYPE_ELEMENT,
669   },
670   {
671     GAME_PANEL_GRAPHIC_6,
672     &game.panel.graphic[5],
673     TYPE_ELEMENT,
674   },
675   {
676     GAME_PANEL_GRAPHIC_7,
677     &game.panel.graphic[6],
678     TYPE_ELEMENT,
679   },
680   {
681     GAME_PANEL_GRAPHIC_8,
682     &game.panel.graphic[7],
683     TYPE_ELEMENT,
684   },
685   {
686     GAME_PANEL_ELEMENT_1,
687     &game.panel.element[0],
688     TYPE_ELEMENT,
689   },
690   {
691     GAME_PANEL_ELEMENT_2,
692     &game.panel.element[1],
693     TYPE_ELEMENT,
694   },
695   {
696     GAME_PANEL_ELEMENT_3,
697     &game.panel.element[2],
698     TYPE_ELEMENT,
699   },
700   {
701     GAME_PANEL_ELEMENT_4,
702     &game.panel.element[3],
703     TYPE_ELEMENT,
704   },
705   {
706     GAME_PANEL_ELEMENT_5,
707     &game.panel.element[4],
708     TYPE_ELEMENT,
709   },
710   {
711     GAME_PANEL_ELEMENT_6,
712     &game.panel.element[5],
713     TYPE_ELEMENT,
714   },
715   {
716     GAME_PANEL_ELEMENT_7,
717     &game.panel.element[6],
718     TYPE_ELEMENT,
719   },
720   {
721     GAME_PANEL_ELEMENT_8,
722     &game.panel.element[7],
723     TYPE_ELEMENT,
724   },
725   {
726     GAME_PANEL_ELEMENT_COUNT_1,
727     &game.panel.element_count[0],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_ELEMENT_COUNT_2,
732     &game.panel.element_count[1],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_ELEMENT_COUNT_3,
737     &game.panel.element_count[2],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_ELEMENT_COUNT_4,
742     &game.panel.element_count[3],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_ELEMENT_COUNT_5,
747     &game.panel.element_count[4],
748     TYPE_INTEGER,
749   },
750   {
751     GAME_PANEL_ELEMENT_COUNT_6,
752     &game.panel.element_count[5],
753     TYPE_INTEGER,
754   },
755   {
756     GAME_PANEL_ELEMENT_COUNT_7,
757     &game.panel.element_count[6],
758     TYPE_INTEGER,
759   },
760   {
761     GAME_PANEL_ELEMENT_COUNT_8,
762     &game.panel.element_count[7],
763     TYPE_INTEGER,
764   },
765   {
766     GAME_PANEL_CE_SCORE_1,
767     &game.panel.ce_score[0],
768     TYPE_INTEGER,
769   },
770   {
771     GAME_PANEL_CE_SCORE_2,
772     &game.panel.ce_score[1],
773     TYPE_INTEGER,
774   },
775   {
776     GAME_PANEL_CE_SCORE_3,
777     &game.panel.ce_score[2],
778     TYPE_INTEGER,
779   },
780   {
781     GAME_PANEL_CE_SCORE_4,
782     &game.panel.ce_score[3],
783     TYPE_INTEGER,
784   },
785   {
786     GAME_PANEL_CE_SCORE_5,
787     &game.panel.ce_score[4],
788     TYPE_INTEGER,
789   },
790   {
791     GAME_PANEL_CE_SCORE_6,
792     &game.panel.ce_score[5],
793     TYPE_INTEGER,
794   },
795   {
796     GAME_PANEL_CE_SCORE_7,
797     &game.panel.ce_score[6],
798     TYPE_INTEGER,
799   },
800   {
801     GAME_PANEL_CE_SCORE_8,
802     &game.panel.ce_score[7],
803     TYPE_INTEGER,
804   },
805   {
806     GAME_PANEL_CE_SCORE_1_ELEMENT,
807     &game.panel.ce_score_element[0],
808     TYPE_ELEMENT,
809   },
810   {
811     GAME_PANEL_CE_SCORE_2_ELEMENT,
812     &game.panel.ce_score_element[1],
813     TYPE_ELEMENT,
814   },
815   {
816     GAME_PANEL_CE_SCORE_3_ELEMENT,
817     &game.panel.ce_score_element[2],
818     TYPE_ELEMENT,
819   },
820   {
821     GAME_PANEL_CE_SCORE_4_ELEMENT,
822     &game.panel.ce_score_element[3],
823     TYPE_ELEMENT,
824   },
825   {
826     GAME_PANEL_CE_SCORE_5_ELEMENT,
827     &game.panel.ce_score_element[4],
828     TYPE_ELEMENT,
829   },
830   {
831     GAME_PANEL_CE_SCORE_6_ELEMENT,
832     &game.panel.ce_score_element[5],
833     TYPE_ELEMENT,
834   },
835   {
836     GAME_PANEL_CE_SCORE_7_ELEMENT,
837     &game.panel.ce_score_element[6],
838     TYPE_ELEMENT,
839   },
840   {
841     GAME_PANEL_CE_SCORE_8_ELEMENT,
842     &game.panel.ce_score_element[7],
843     TYPE_ELEMENT,
844   },
845   {
846     GAME_PANEL_PLAYER_NAME,
847     &game.panel.player_name,
848     TYPE_STRING,
849   },
850   {
851     GAME_PANEL_LEVEL_NAME,
852     &game.panel.level_name,
853     TYPE_STRING,
854   },
855   {
856     GAME_PANEL_LEVEL_AUTHOR,
857     &game.panel.level_author,
858     TYPE_STRING,
859   },
860
861   {
862     -1,
863     NULL,
864     -1,
865   }
866 };
867 #endif
868
869
870 /* values for delayed check of falling and moving elements and for collision */
871 #define CHECK_DELAY_MOVING      3
872 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
873 #define CHECK_DELAY_COLLISION   2
874 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
875
876 /* values for initial player move delay (initial delay counter value) */
877 #define INITIAL_MOVE_DELAY_OFF  -1
878 #define INITIAL_MOVE_DELAY_ON   0
879
880 /* values for player movement speed (which is in fact a delay value) */
881 #define MOVE_DELAY_MIN_SPEED    32
882 #define MOVE_DELAY_NORMAL_SPEED 8
883 #define MOVE_DELAY_HIGH_SPEED   4
884 #define MOVE_DELAY_MAX_SPEED    1
885
886 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
887 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
888
889 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
890 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
891
892 /* values for other actions */
893 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
894 #define MOVE_STEPSIZE_MIN       (1)
895 #define MOVE_STEPSIZE_MAX       (TILEX)
896
897 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
898 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
899
900 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
901
902 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
903                                  RND(element_info[e].push_delay_random))
904 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
905                                  RND(element_info[e].drop_delay_random))
906 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
907                                  RND(element_info[e].move_delay_random))
908 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
909                                     (element_info[e].move_delay_random))
910 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
911                                  RND(element_info[e].ce_value_random_initial))
912 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
913 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
914                                  RND((c)->delay_random * (c)->delay_frames))
915 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
916                                  RND((c)->delay_random))
917
918
919 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
920          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
921
922 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
923         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
924          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
925          (be) + (e) - EL_SELF)
926
927 #define GET_PLAYER_FROM_BITS(p)                                         \
928         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
929
930 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
931         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
932          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
933          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
934          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
935          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
936          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
937          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
938          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
939          (e))
940
941 #define CAN_GROW_INTO(e)                                                \
942         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
943
944 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
945                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Feld[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (condition) ||                  \
963                                         (CAN_MOVE_INTO_ACID(e) &&       \
964                                          Feld[x][y] == EL_ACID) ||      \
965                                         (DONT_COLLIDE_WITH(e) &&        \
966                                          IS_PLAYER(x, y) &&             \
967                                          !PLAYER_ENEMY_PROTECTED(x, y))))
968
969 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
971
972 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
974
975 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
977
978 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
979         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
980                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
981
982 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984
985 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
987
988 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
989         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
990
991 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
993
994 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
996
997 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
999                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1000                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1001                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1002                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1003 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1008
1009 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1013         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1014                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1015
1016 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1017
1018 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1019                 (!IS_PLAYER(x, y) &&                                    \
1020                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1021
1022 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1023         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1024
1025 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1026 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1027
1028 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1029 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1030 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1031 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1032
1033 /* game button identifiers */
1034 #define GAME_CTRL_ID_STOP               0
1035 #define GAME_CTRL_ID_PAUSE              1
1036 #define GAME_CTRL_ID_PLAY               2
1037 #define SOUND_CTRL_ID_MUSIC             3
1038 #define SOUND_CTRL_ID_LOOPS             4
1039 #define SOUND_CTRL_ID_SIMPLE            5
1040
1041 #define NUM_GAME_BUTTONS                6
1042
1043
1044 /* forward declaration for internal use */
1045
1046 static void CreateField(int, int, int);
1047
1048 static void ResetGfxAnimation(int, int);
1049
1050 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1051 static void AdvanceFrameAndPlayerCounters(int);
1052
1053 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1054 static boolean MovePlayer(struct PlayerInfo *, int, int);
1055 static void ScrollPlayer(struct PlayerInfo *, int);
1056 static void ScrollScreen(struct PlayerInfo *, int);
1057
1058 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1059 static boolean DigFieldByCE(int, int, int);
1060 static boolean SnapField(struct PlayerInfo *, int, int);
1061 static boolean DropElement(struct PlayerInfo *);
1062
1063 static void InitBeltMovement(void);
1064 static void CloseAllOpenTimegates(void);
1065 static void CheckGravityMovement(struct PlayerInfo *);
1066 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1067 static void KillPlayerUnlessEnemyProtected(int, int);
1068 static void KillPlayerUnlessExplosionProtected(int, int);
1069
1070 static void TestIfPlayerTouchesCustomElement(int, int);
1071 static void TestIfElementTouchesCustomElement(int, int);
1072 static void TestIfElementHitsCustomElement(int, int, int);
1073 #if 0
1074 static void TestIfElementSmashesCustomElement(int, int, int);
1075 #endif
1076
1077 static void HandleElementChange(int, int, int);
1078 static void ExecuteCustomElementAction(int, int, int, int);
1079 static boolean ChangeElement(int, int, int, int);
1080
1081 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1082 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1083         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1084 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1085         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1086 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1087         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1088 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1090
1091 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1092 #define CheckElementChange(x, y, e, te, ev)                             \
1093         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1094 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1095         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1096 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1097         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1098
1099 static void PlayLevelSound(int, int, int);
1100 static void PlayLevelSoundNearest(int, int, int);
1101 static void PlayLevelSoundAction(int, int, int);
1102 static void PlayLevelSoundElementAction(int, int, int, int);
1103 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1104 static void PlayLevelSoundActionIfLoop(int, int, int);
1105 static void StopLevelSoundActionIfLoop(int, int, int);
1106 static void PlayLevelMusic();
1107
1108 static void MapGameButtons();
1109 static void HandleGameButtons(struct GadgetInfo *);
1110
1111 int AmoebeNachbarNr(int, int);
1112 void AmoebeUmwandeln(int, int);
1113 void ContinueMoving(int, int);
1114 void Bang(int, int);
1115 void InitMovDir(int, int);
1116 void InitAmoebaNr(int, int);
1117 int NewHiScore(void);
1118
1119 void TestIfGoodThingHitsBadThing(int, int, int);
1120 void TestIfBadThingHitsGoodThing(int, int, int);
1121 void TestIfPlayerTouchesBadThing(int, int);
1122 void TestIfPlayerRunsIntoBadThing(int, int, int);
1123 void TestIfBadThingTouchesPlayer(int, int);
1124 void TestIfBadThingRunsIntoPlayer(int, int, int);
1125 void TestIfFriendTouchesBadThing(int, int);
1126 void TestIfBadThingTouchesFriend(int, int);
1127 void TestIfBadThingTouchesOtherBadThing(int, int);
1128
1129 void KillPlayer(struct PlayerInfo *);
1130 void BuryPlayer(struct PlayerInfo *);
1131 void RemovePlayer(struct PlayerInfo *);
1132
1133 static int getInvisibleActiveFromInvisibleElement(int);
1134 static int getInvisibleFromInvisibleActiveElement(int);
1135
1136 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1137
1138 /* for detection of endless loops, caused by custom element programming */
1139 /* (using maximal playfield width x 10 is just a rough approximation) */
1140 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1141
1142 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1143 {                                                                       \
1144   if (recursion_loop_detected)                                          \
1145     return (rc);                                                        \
1146                                                                         \
1147   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1148   {                                                                     \
1149     recursion_loop_detected = TRUE;                                     \
1150     recursion_loop_element = (e);                                       \
1151   }                                                                     \
1152                                                                         \
1153   recursion_loop_depth++;                                               \
1154 }
1155
1156 #define RECURSION_LOOP_DETECTION_END()                                  \
1157 {                                                                       \
1158   recursion_loop_depth--;                                               \
1159 }
1160
1161 static int recursion_loop_depth;
1162 static boolean recursion_loop_detected;
1163 static boolean recursion_loop_element;
1164
1165
1166 /* ------------------------------------------------------------------------- */
1167 /* definition of elements that automatically change to other elements after  */
1168 /* a specified time, eventually calling a function when changing             */
1169 /* ------------------------------------------------------------------------- */
1170
1171 /* forward declaration for changer functions */
1172 static void InitBuggyBase(int, int);
1173 static void WarnBuggyBase(int, int);
1174
1175 static void InitTrap(int, int);
1176 static void ActivateTrap(int, int);
1177 static void ChangeActiveTrap(int, int);
1178
1179 static void InitRobotWheel(int, int);
1180 static void RunRobotWheel(int, int);
1181 static void StopRobotWheel(int, int);
1182
1183 static void InitTimegateWheel(int, int);
1184 static void RunTimegateWheel(int, int);
1185
1186 static void InitMagicBallDelay(int, int);
1187 static void ActivateMagicBall(int, int);
1188
1189 struct ChangingElementInfo
1190 {
1191   int element;
1192   int target_element;
1193   int change_delay;
1194   void (*pre_change_function)(int x, int y);
1195   void (*change_function)(int x, int y);
1196   void (*post_change_function)(int x, int y);
1197 };
1198
1199 static struct ChangingElementInfo change_delay_list[] =
1200 {
1201   {
1202     EL_NUT_BREAKING,
1203     EL_EMERALD,
1204     6,
1205     NULL,
1206     NULL,
1207     NULL
1208   },
1209   {
1210     EL_PEARL_BREAKING,
1211     EL_EMPTY,
1212     8,
1213     NULL,
1214     NULL,
1215     NULL
1216   },
1217   {
1218     EL_EXIT_OPENING,
1219     EL_EXIT_OPEN,
1220     29,
1221     NULL,
1222     NULL,
1223     NULL
1224   },
1225   {
1226     EL_EXIT_CLOSING,
1227     EL_EXIT_CLOSED,
1228     29,
1229     NULL,
1230     NULL,
1231     NULL
1232   },
1233   {
1234     EL_STEEL_EXIT_OPENING,
1235     EL_STEEL_EXIT_OPEN,
1236     29,
1237     NULL,
1238     NULL,
1239     NULL
1240   },
1241   {
1242     EL_STEEL_EXIT_CLOSING,
1243     EL_STEEL_EXIT_CLOSED,
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_EXIT_OPENING,
1251     EL_EM_EXIT_OPEN,
1252     29,
1253     NULL,
1254     NULL,
1255     NULL
1256   },
1257   {
1258     EL_EM_EXIT_CLOSING,
1259 #if 1
1260     EL_EMPTY,
1261 #else
1262     EL_EM_EXIT_CLOSED,
1263 #endif
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_EM_STEEL_EXIT_OPENING,
1271     EL_EM_STEEL_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_EM_STEEL_EXIT_CLOSING,
1279 #if 1
1280     EL_STEELWALL,
1281 #else
1282     EL_EM_STEEL_EXIT_CLOSED,
1283 #endif
1284     29,
1285     NULL,
1286     NULL,
1287     NULL
1288   },
1289   {
1290     EL_SP_EXIT_OPENING,
1291     EL_SP_EXIT_OPEN,
1292     29,
1293     NULL,
1294     NULL,
1295     NULL
1296   },
1297   {
1298     EL_SP_EXIT_CLOSING,
1299     EL_SP_EXIT_CLOSED,
1300     29,
1301     NULL,
1302     NULL,
1303     NULL
1304   },
1305   {
1306     EL_SWITCHGATE_OPENING,
1307     EL_SWITCHGATE_OPEN,
1308     29,
1309     NULL,
1310     NULL,
1311     NULL
1312   },
1313   {
1314     EL_SWITCHGATE_CLOSING,
1315     EL_SWITCHGATE_CLOSED,
1316     29,
1317     NULL,
1318     NULL,
1319     NULL
1320   },
1321   {
1322     EL_TIMEGATE_OPENING,
1323     EL_TIMEGATE_OPEN,
1324     29,
1325     NULL,
1326     NULL,
1327     NULL
1328   },
1329   {
1330     EL_TIMEGATE_CLOSING,
1331     EL_TIMEGATE_CLOSED,
1332     29,
1333     NULL,
1334     NULL,
1335     NULL
1336   },
1337
1338   {
1339     EL_ACID_SPLASH_LEFT,
1340     EL_EMPTY,
1341     8,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346   {
1347     EL_ACID_SPLASH_RIGHT,
1348     EL_EMPTY,
1349     8,
1350     NULL,
1351     NULL,
1352     NULL
1353   },
1354   {
1355     EL_SP_BUGGY_BASE,
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     0,
1358     InitBuggyBase,
1359     NULL,
1360     NULL
1361   },
1362   {
1363     EL_SP_BUGGY_BASE_ACTIVATING,
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     0,
1366     InitBuggyBase,
1367     NULL,
1368     NULL
1369   },
1370   {
1371     EL_SP_BUGGY_BASE_ACTIVE,
1372     EL_SP_BUGGY_BASE,
1373     0,
1374     InitBuggyBase,
1375     WarnBuggyBase,
1376     NULL
1377   },
1378   {
1379     EL_TRAP,
1380     EL_TRAP_ACTIVE,
1381     0,
1382     InitTrap,
1383     NULL,
1384     ActivateTrap
1385   },
1386   {
1387     EL_TRAP_ACTIVE,
1388     EL_TRAP,
1389     31,
1390     NULL,
1391     ChangeActiveTrap,
1392     NULL
1393   },
1394   {
1395     EL_ROBOT_WHEEL_ACTIVE,
1396     EL_ROBOT_WHEEL,
1397     0,
1398     InitRobotWheel,
1399     RunRobotWheel,
1400     StopRobotWheel
1401   },
1402   {
1403     EL_TIMEGATE_SWITCH_ACTIVE,
1404     EL_TIMEGATE_SWITCH,
1405     0,
1406     InitTimegateWheel,
1407     RunTimegateWheel,
1408     NULL
1409   },
1410   {
1411     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1412     EL_DC_TIMEGATE_SWITCH,
1413     0,
1414     InitTimegateWheel,
1415     RunTimegateWheel,
1416     NULL
1417   },
1418   {
1419     EL_EMC_MAGIC_BALL_ACTIVE,
1420     EL_EMC_MAGIC_BALL_ACTIVE,
1421     0,
1422     InitMagicBallDelay,
1423     NULL,
1424     ActivateMagicBall
1425   },
1426   {
1427     EL_EMC_SPRING_BUMPER_ACTIVE,
1428     EL_EMC_SPRING_BUMPER,
1429     8,
1430     NULL,
1431     NULL,
1432     NULL
1433   },
1434   {
1435     EL_DIAGONAL_SHRINKING,
1436     EL_UNDEFINED,
1437     0,
1438     NULL,
1439     NULL,
1440     NULL
1441   },
1442   {
1443     EL_DIAGONAL_GROWING,
1444     EL_UNDEFINED,
1445     0,
1446     NULL,
1447     NULL,
1448     NULL,
1449   },
1450
1451   {
1452     EL_UNDEFINED,
1453     EL_UNDEFINED,
1454     -1,
1455     NULL,
1456     NULL,
1457     NULL
1458   }
1459 };
1460
1461 struct
1462 {
1463   int element;
1464   int push_delay_fixed, push_delay_random;
1465 }
1466 push_delay_list[] =
1467 {
1468   { EL_SPRING,                  0, 0 },
1469   { EL_BALLOON,                 0, 0 },
1470
1471   { EL_SOKOBAN_OBJECT,          2, 0 },
1472   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1473   { EL_SATELLITE,               2, 0 },
1474   { EL_SP_DISK_YELLOW,          2, 0 },
1475
1476   { EL_UNDEFINED,               0, 0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int move_stepsize;
1483 }
1484 move_stepsize_list[] =
1485 {
1486   { EL_AMOEBA_DROP,             2 },
1487   { EL_AMOEBA_DROPPING,         2 },
1488   { EL_QUICKSAND_FILLING,       1 },
1489   { EL_QUICKSAND_EMPTYING,      1 },
1490   { EL_QUICKSAND_FAST_FILLING,  2 },
1491   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1492   { EL_MAGIC_WALL_FILLING,      2 },
1493   { EL_MAGIC_WALL_EMPTYING,     2 },
1494   { EL_BD_MAGIC_WALL_FILLING,   2 },
1495   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1496   { EL_DC_MAGIC_WALL_FILLING,   2 },
1497   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int count;
1506 }
1507 collect_count_list[] =
1508 {
1509   { EL_EMERALD,                 1 },
1510   { EL_BD_DIAMOND,              1 },
1511   { EL_EMERALD_YELLOW,          1 },
1512   { EL_EMERALD_RED,             1 },
1513   { EL_EMERALD_PURPLE,          1 },
1514   { EL_DIAMOND,                 3 },
1515   { EL_SP_INFOTRON,             1 },
1516   { EL_PEARL,                   5 },
1517   { EL_CRYSTAL,                 8 },
1518
1519   { EL_UNDEFINED,               0 },
1520 };
1521
1522 struct
1523 {
1524   int element;
1525   int direction;
1526 }
1527 access_direction_list[] =
1528 {
1529   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1531   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1532   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1533   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1534   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1535   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1536   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1537   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1538   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1539   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1540
1541   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1542   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1543   { EL_SP_PORT_UP,                                                   MV_DOWN },
1544   { EL_SP_PORT_DOWN,                                         MV_UP           },
1545   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1546   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1547   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1549   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1550   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1551   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1552   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1553   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1554   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1555   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1556   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1557   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1558   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1559   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1560
1561   { EL_UNDEFINED,                       MV_NONE                              }
1562 };
1563
1564 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1565
1566 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1567 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1568 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1569                                  IS_JUST_CHANGING(x, y))
1570
1571 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1572
1573 /* static variables for playfield scan mode (scanning forward or backward) */
1574 static int playfield_scan_start_x = 0;
1575 static int playfield_scan_start_y = 0;
1576 static int playfield_scan_delta_x = 1;
1577 static int playfield_scan_delta_y = 1;
1578
1579 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1580                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1581                                      (y) += playfield_scan_delta_y)     \
1582                                 for ((x) = playfield_scan_start_x;      \
1583                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1584                                      (x) += playfield_scan_delta_x)
1585
1586 #ifdef DEBUG
1587 void DEBUG_SetMaximumDynamite()
1588 {
1589   int i;
1590
1591   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1592     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1593       local_player->inventory_element[local_player->inventory_size++] =
1594         EL_DYNAMITE;
1595 }
1596 #endif
1597
1598 static void InitPlayfieldScanModeVars()
1599 {
1600   if (game.use_reverse_scan_direction)
1601   {
1602     playfield_scan_start_x = lev_fieldx - 1;
1603     playfield_scan_start_y = lev_fieldy - 1;
1604
1605     playfield_scan_delta_x = -1;
1606     playfield_scan_delta_y = -1;
1607   }
1608   else
1609   {
1610     playfield_scan_start_x = 0;
1611     playfield_scan_start_y = 0;
1612
1613     playfield_scan_delta_x = 1;
1614     playfield_scan_delta_y = 1;
1615   }
1616 }
1617
1618 static void InitPlayfieldScanMode(int mode)
1619 {
1620   game.use_reverse_scan_direction =
1621     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1622
1623   InitPlayfieldScanModeVars();
1624 }
1625
1626 static int get_move_delay_from_stepsize(int move_stepsize)
1627 {
1628   move_stepsize =
1629     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1630
1631   /* make sure that stepsize value is always a power of 2 */
1632   move_stepsize = (1 << log_2(move_stepsize));
1633
1634   return TILEX / move_stepsize;
1635 }
1636
1637 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1638                                boolean init_game)
1639 {
1640   int player_nr = player->index_nr;
1641   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1642   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1643
1644   /* do no immediately change move delay -- the player might just be moving */
1645   player->move_delay_value_next = move_delay;
1646
1647   /* information if player can move must be set separately */
1648   player->cannot_move = cannot_move;
1649
1650   if (init_game)
1651   {
1652     player->move_delay       = game.initial_move_delay[player_nr];
1653     player->move_delay_value = game.initial_move_delay_value[player_nr];
1654
1655     player->move_delay_value_next = -1;
1656
1657     player->move_delay_reset_counter = 0;
1658   }
1659 }
1660
1661 void GetPlayerConfig()
1662 {
1663   GameFrameDelay = setup.game_frame_delay;
1664
1665   if (!audio.sound_available)
1666     setup.sound_simple = FALSE;
1667
1668   if (!audio.loops_available)
1669     setup.sound_loops = FALSE;
1670
1671   if (!audio.music_available)
1672     setup.sound_music = FALSE;
1673
1674   if (!video.fullscreen_available)
1675     setup.fullscreen = FALSE;
1676
1677   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1678
1679   SetAudioMode(setup.sound);
1680   InitJoysticks();
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void InitPlayerField(int x, int y, int element, boolean init_game)
1710 {
1711   if (element == EL_SP_MURPHY)
1712   {
1713     if (init_game)
1714     {
1715       if (stored_player[0].present)
1716       {
1717         Feld[x][y] = EL_SP_MURPHY_CLONE;
1718
1719         return;
1720       }
1721       else
1722       {
1723         stored_player[0].use_murphy = TRUE;
1724
1725         if (!level.use_artwork_element[0])
1726           stored_player[0].artwork_element = EL_SP_MURPHY;
1727       }
1728
1729       Feld[x][y] = EL_PLAYER_1;
1730     }
1731   }
1732
1733   if (init_game)
1734   {
1735     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1736     int jx = player->jx, jy = player->jy;
1737
1738     player->present = TRUE;
1739
1740     player->block_last_field = (element == EL_SP_MURPHY ?
1741                                 level.sp_block_last_field :
1742                                 level.block_last_field);
1743
1744     /* ---------- initialize player's last field block delay --------------- */
1745
1746     /* always start with reliable default value (no adjustment needed) */
1747     player->block_delay_adjustment = 0;
1748
1749     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1750     if (player->block_last_field && element == EL_SP_MURPHY)
1751       player->block_delay_adjustment = 1;
1752
1753     /* special case 2: in game engines before 3.1.1, blocking was different */
1754     if (game.use_block_last_field_bug)
1755       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1756
1757     if (!options.network || player->connected)
1758     {
1759       player->active = TRUE;
1760
1761       /* remove potentially duplicate players */
1762       if (StorePlayer[jx][jy] == Feld[x][y])
1763         StorePlayer[jx][jy] = 0;
1764
1765       StorePlayer[x][y] = Feld[x][y];
1766
1767       if (options.debug)
1768       {
1769         printf("Player %d activated.\n", player->element_nr);
1770         printf("[Local player is %d and currently %s.]\n",
1771                local_player->element_nr,
1772                local_player->active ? "active" : "not active");
1773       }
1774     }
1775
1776     Feld[x][y] = EL_EMPTY;
1777
1778     player->jx = player->last_jx = x;
1779     player->jy = player->last_jy = y;
1780   }
1781 }
1782
1783 static void InitField(int x, int y, boolean init_game)
1784 {
1785   int element = Feld[x][y];
1786
1787   switch (element)
1788   {
1789     case EL_SP_MURPHY:
1790     case EL_PLAYER_1:
1791     case EL_PLAYER_2:
1792     case EL_PLAYER_3:
1793     case EL_PLAYER_4:
1794       InitPlayerField(x, y, element, init_game);
1795       break;
1796
1797     case EL_SOKOBAN_FIELD_PLAYER:
1798       element = Feld[x][y] = EL_PLAYER_1;
1799       InitField(x, y, init_game);
1800
1801       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1802       InitField(x, y, init_game);
1803       break;
1804
1805     case EL_SOKOBAN_FIELD_EMPTY:
1806       local_player->sokobanfields_still_needed++;
1807       break;
1808
1809     case EL_STONEBLOCK:
1810       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1811         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1812       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1813         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1814       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1815         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1816       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1817         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1818       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1819         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1820       break;
1821
1822     case EL_BUG:
1823     case EL_BUG_RIGHT:
1824     case EL_BUG_UP:
1825     case EL_BUG_LEFT:
1826     case EL_BUG_DOWN:
1827     case EL_SPACESHIP:
1828     case EL_SPACESHIP_RIGHT:
1829     case EL_SPACESHIP_UP:
1830     case EL_SPACESHIP_LEFT:
1831     case EL_SPACESHIP_DOWN:
1832     case EL_BD_BUTTERFLY:
1833     case EL_BD_BUTTERFLY_RIGHT:
1834     case EL_BD_BUTTERFLY_UP:
1835     case EL_BD_BUTTERFLY_LEFT:
1836     case EL_BD_BUTTERFLY_DOWN:
1837     case EL_BD_FIREFLY:
1838     case EL_BD_FIREFLY_RIGHT:
1839     case EL_BD_FIREFLY_UP:
1840     case EL_BD_FIREFLY_LEFT:
1841     case EL_BD_FIREFLY_DOWN:
1842     case EL_PACMAN_RIGHT:
1843     case EL_PACMAN_UP:
1844     case EL_PACMAN_LEFT:
1845     case EL_PACMAN_DOWN:
1846     case EL_YAMYAM:
1847     case EL_YAMYAM_LEFT:
1848     case EL_YAMYAM_RIGHT:
1849     case EL_YAMYAM_UP:
1850     case EL_YAMYAM_DOWN:
1851     case EL_DARK_YAMYAM:
1852     case EL_ROBOT:
1853     case EL_PACMAN:
1854     case EL_SP_SNIKSNAK:
1855     case EL_SP_ELECTRON:
1856     case EL_MOLE:
1857     case EL_MOLE_LEFT:
1858     case EL_MOLE_RIGHT:
1859     case EL_MOLE_UP:
1860     case EL_MOLE_DOWN:
1861       InitMovDir(x, y);
1862       break;
1863
1864     case EL_AMOEBA_FULL:
1865     case EL_BD_AMOEBA:
1866       InitAmoebaNr(x, y);
1867       break;
1868
1869     case EL_AMOEBA_DROP:
1870       if (y == lev_fieldy - 1)
1871       {
1872         Feld[x][y] = EL_AMOEBA_GROWING;
1873         Store[x][y] = EL_AMOEBA_WET;
1874       }
1875       break;
1876
1877     case EL_DYNAMITE_ACTIVE:
1878     case EL_SP_DISK_RED_ACTIVE:
1879     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1880     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1881     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1882     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1883       MovDelay[x][y] = 96;
1884       break;
1885
1886     case EL_EM_DYNAMITE_ACTIVE:
1887       MovDelay[x][y] = 32;
1888       break;
1889
1890     case EL_LAMP:
1891       local_player->lights_still_needed++;
1892       break;
1893
1894     case EL_PENGUIN:
1895       local_player->friends_still_needed++;
1896       break;
1897
1898     case EL_PIG:
1899     case EL_DRAGON:
1900       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1901       break;
1902
1903     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1904     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1905     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1906     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1907     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1908     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1909     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1910     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1911     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1912     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1913     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1914     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1915       if (init_game)
1916       {
1917         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1918         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1919         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1920
1921         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1922         {
1923           game.belt_dir[belt_nr] = belt_dir;
1924           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1925         }
1926         else    /* more than one switch -- set it like the first switch */
1927         {
1928           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1929         }
1930       }
1931       break;
1932
1933 #if !USE_BOTH_SWITCHGATE_SWITCHES
1934     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1935       if (init_game)
1936         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1937       break;
1938
1939     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1940       if (init_game)
1941         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1942       break;
1943 #endif
1944
1945     case EL_LIGHT_SWITCH_ACTIVE:
1946       if (init_game)
1947         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1948       break;
1949
1950     case EL_INVISIBLE_STEELWALL:
1951     case EL_INVISIBLE_WALL:
1952     case EL_INVISIBLE_SAND:
1953       if (game.light_time_left > 0 ||
1954           game.lenses_time_left > 0)
1955         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1956       break;
1957
1958     case EL_EMC_MAGIC_BALL:
1959       if (game.ball_state)
1960         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL_SWITCH:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1966       break;
1967
1968     case EL_TRIGGER_PLAYER:
1969     case EL_TRIGGER_ELEMENT:
1970     case EL_TRIGGER_CE_VALUE:
1971     case EL_TRIGGER_CE_SCORE:
1972     case EL_SELF:
1973     case EL_ANY_ELEMENT:
1974     case EL_CURRENT_CE_VALUE:
1975     case EL_CURRENT_CE_SCORE:
1976     case EL_PREV_CE_1:
1977     case EL_PREV_CE_2:
1978     case EL_PREV_CE_3:
1979     case EL_PREV_CE_4:
1980     case EL_PREV_CE_5:
1981     case EL_PREV_CE_6:
1982     case EL_PREV_CE_7:
1983     case EL_PREV_CE_8:
1984     case EL_NEXT_CE_1:
1985     case EL_NEXT_CE_2:
1986     case EL_NEXT_CE_3:
1987     case EL_NEXT_CE_4:
1988     case EL_NEXT_CE_5:
1989     case EL_NEXT_CE_6:
1990     case EL_NEXT_CE_7:
1991     case EL_NEXT_CE_8:
1992       /* reference elements should not be used on the playfield */
1993       Feld[x][y] = EL_EMPTY;
1994       break;
1995
1996     default:
1997       if (IS_CUSTOM_ELEMENT(element))
1998       {
1999         if (CAN_MOVE(element))
2000           InitMovDir(x, y);
2001
2002 #if USE_NEW_CUSTOM_VALUE
2003         if (!element_info[element].use_last_ce_value || init_game)
2004           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2005 #endif
2006       }
2007       else if (IS_GROUP_ELEMENT(element))
2008       {
2009         Feld[x][y] = GetElementFromGroupElement(element);
2010
2011         InitField(x, y, init_game);
2012       }
2013
2014       break;
2015   }
2016
2017   if (!init_game)
2018     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2019 }
2020
2021 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2022 {
2023   InitField(x, y, init_game);
2024
2025   /* not needed to call InitMovDir() -- already done by InitField()! */
2026   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2027       CAN_MOVE(Feld[x][y]))
2028     InitMovDir(x, y);
2029 }
2030
2031 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2032 {
2033   int old_element = Feld[x][y];
2034
2035   InitField(x, y, init_game);
2036
2037   /* not needed to call InitMovDir() -- already done by InitField()! */
2038   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2039       CAN_MOVE(old_element) &&
2040       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2041     InitMovDir(x, y);
2042
2043   /* this case is in fact a combination of not less than three bugs:
2044      first, it calls InitMovDir() for elements that can move, although this is
2045      already done by InitField(); then, it checks the element that was at this
2046      field _before_ the call to InitField() (which can change it); lastly, it
2047      was not called for "mole with direction" elements, which were treated as
2048      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2049   */
2050 }
2051
2052 #if 1
2053
2054 static int get_key_element_from_nr(int key_nr)
2055 {
2056   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2057                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2058                           EL_EM_KEY_1 : EL_KEY_1);
2059
2060   return key_base_element + key_nr;
2061 }
2062
2063 static int get_next_dropped_element(struct PlayerInfo *player)
2064 {
2065   return (player->inventory_size > 0 ?
2066           player->inventory_element[player->inventory_size - 1] :
2067           player->inventory_infinite_element != EL_UNDEFINED ?
2068           player->inventory_infinite_element :
2069           player->dynabombs_left > 0 ?
2070           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2071           EL_UNDEFINED);
2072 }
2073
2074 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2075 {
2076   /* pos >= 0: get element from bottom of the stack;
2077      pos <  0: get element from top of the stack */
2078
2079   if (pos < 0)
2080   {
2081     int min_inventory_size = -pos;
2082     int inventory_pos = player->inventory_size - min_inventory_size;
2083     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2084
2085     return (player->inventory_size >= min_inventory_size ?
2086             player->inventory_element[inventory_pos] :
2087             player->inventory_infinite_element != EL_UNDEFINED ?
2088             player->inventory_infinite_element :
2089             player->dynabombs_left >= min_dynabombs_left ?
2090             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2091             EL_UNDEFINED);
2092   }
2093   else
2094   {
2095     int min_dynabombs_left = pos + 1;
2096     int min_inventory_size = pos + 1 - player->dynabombs_left;
2097     int inventory_pos = pos - player->dynabombs_left;
2098
2099     return (player->inventory_infinite_element != EL_UNDEFINED ?
2100             player->inventory_infinite_element :
2101             player->dynabombs_left >= min_dynabombs_left ?
2102             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2103             player->inventory_size >= min_inventory_size ?
2104             player->inventory_element[inventory_pos] :
2105             EL_UNDEFINED);
2106   }
2107 }
2108
2109 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2110 {
2111   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2112   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2113   int compare_result;
2114
2115   if (gpo1->sort_priority != gpo2->sort_priority)
2116     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2117   else
2118     compare_result = gpo1->nr - gpo2->nr;
2119
2120   return compare_result;
2121 }
2122
2123 void InitGameControlValues()
2124 {
2125   int i;
2126
2127   for (i = 0; game_panel_controls[i].nr != -1; i++)
2128   {
2129     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2130     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2131     struct TextPosInfo *pos = gpc->pos;
2132     int nr = gpc->nr;
2133     int type = gpc->type;
2134
2135     if (nr != i)
2136     {
2137       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2138       Error(ERR_EXIT, "this should not happen -- please debug");
2139     }
2140
2141     /* force update of game controls after initialization */
2142     gpc->value = gpc->last_value = -1;
2143     gpc->frame = gpc->last_frame = -1;
2144     gpc->gfx_frame = -1;
2145
2146     /* determine panel value width for later calculation of alignment */
2147     if (type == TYPE_INTEGER || type == TYPE_STRING)
2148     {
2149       pos->width = pos->size * getFontWidth(pos->font);
2150       pos->height = getFontHeight(pos->font);
2151     }
2152     else if (type == TYPE_ELEMENT)
2153     {
2154       pos->width = pos->size;
2155       pos->height = pos->size;
2156     }
2157
2158     /* fill structure for game panel draw order */
2159     gpo->nr = gpc->nr;
2160     gpo->sort_priority = pos->sort_priority;
2161   }
2162
2163   /* sort game panel controls according to sort_priority and control number */
2164   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2165         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2166 }
2167
2168 void UpdatePlayfieldElementCount()
2169 {
2170   boolean use_element_count = FALSE;
2171   int i, j, x, y;
2172
2173   /* first check if it is needed at all to calculate playfield element count */
2174   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2175     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2176       use_element_count = TRUE;
2177
2178   if (!use_element_count)
2179     return;
2180
2181   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2182     element_info[i].element_count = 0;
2183
2184   SCAN_PLAYFIELD(x, y)
2185   {
2186     element_info[Feld[x][y]].element_count++;
2187   }
2188
2189   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2190     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2191       if (IS_IN_GROUP(j, i))
2192         element_info[EL_GROUP_START + i].element_count +=
2193           element_info[j].element_count;
2194 }
2195
2196 void UpdateGameControlValues()
2197 {
2198   int i, k;
2199   int time = (local_player->LevelSolved ?
2200               local_player->LevelSolved_CountingTime :
2201               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202               level.native_em_level->lev->time :
2203               level.time == 0 ? TimePlayed : TimeLeft);
2204   int score = (local_player->LevelSolved ?
2205                local_player->LevelSolved_CountingScore :
2206                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                level.native_em_level->lev->score :
2208                local_player->score);
2209   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210               level.native_em_level->lev->required :
2211               local_player->gems_still_needed);
2212   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213                      level.native_em_level->lev->required > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217
2218   UpdatePlayfieldElementCount();
2219
2220   /* update game panel control values */
2221
2222   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2223   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2224
2225   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2226   for (i = 0; i < MAX_NUM_KEYS; i++)
2227     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2228   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2230
2231   if (game.centered_player_nr == -1)
2232   {
2233     for (i = 0; i < MAX_PLAYERS; i++)
2234     {
2235       for (k = 0; k < MAX_NUM_KEYS; k++)
2236       {
2237         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2238         {
2239           if (level.native_em_level->ply[i]->keys & (1 << k))
2240             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2241               get_key_element_from_nr(k);
2242         }
2243         else if (stored_player[i].key[k])
2244           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2245             get_key_element_from_nr(k);
2246       }
2247
2248       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2249         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250           level.native_em_level->ply[i]->dynamite;
2251       else
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           stored_player[i].inventory_size;
2254
2255       if (stored_player[i].num_white_keys > 0)
2256         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257           EL_DC_KEY_WHITE;
2258
2259       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260         stored_player[i].num_white_keys;
2261     }
2262   }
2263   else
2264   {
2265     int player_nr = game.centered_player_nr;
2266
2267     for (k = 0; k < MAX_NUM_KEYS; k++)
2268     {
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270       {
2271         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273             get_key_element_from_nr(k);
2274       }
2275       else if (stored_player[player_nr].key[k])
2276         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277           get_key_element_from_nr(k);
2278     }
2279
2280     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282         level.native_em_level->ply[player_nr]->dynamite;
2283     else
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285         stored_player[player_nr].inventory_size;
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2312     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2313      EL_EMPTY);
2314   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2315     local_player->shield_normal_time_left;
2316   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2317     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2318      EL_EMPTY);
2319   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2320     local_player->shield_deadly_time_left;
2321
2322   game_panel_controls[GAME_PANEL_EXIT].value =
2323     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2324
2325   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2326     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2327   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2328     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2329      EL_EMC_MAGIC_BALL_SWITCH);
2330
2331   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2332     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2333   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2334     game.light_time_left;
2335
2336   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2337     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2338   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2339     game.timegate_time_left;
2340
2341   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2342     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2343
2344   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2345     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2346   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2347     game.lenses_time_left;
2348
2349   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2350     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2351   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2352     game.magnify_time_left;
2353
2354   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2355     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2356      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2357      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2358      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2359      EL_BALLOON_SWITCH_NONE);
2360
2361   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2362     local_player->dynabomb_count;
2363   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2364     local_player->dynabomb_size;
2365   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2366     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2367
2368   game_panel_controls[GAME_PANEL_PENGUINS].value =
2369     local_player->friends_still_needed;
2370
2371   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2372     local_player->sokobanfields_still_needed;
2373   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2374     local_player->sokobanfields_still_needed;
2375
2376   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2377     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2378
2379   for (i = 0; i < NUM_BELTS; i++)
2380   {
2381     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2382       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2383        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2384     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2385       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2386   }
2387
2388   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2389     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2390   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2391     game.magic_wall_time_left;
2392
2393 #if USE_PLAYER_GRAVITY
2394   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2395     local_player->gravity;
2396 #else
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2398 #endif
2399
2400   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406        game.panel.element[i].id : EL_UNDEFINED);
2407
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411        element_info[game.panel.element_count[i].id].element_count : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416        element_info[game.panel.ce_score[i].id].collect_score : 0);
2417
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421        element_info[game.panel.ce_score_element[i].id].collect_score :
2422        EL_UNDEFINED);
2423
2424   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2427
2428   /* update game panel control frames */
2429
2430   for (i = 0; game_panel_controls[i].nr != -1; i++)
2431   {
2432     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2433
2434     if (gpc->type == TYPE_ELEMENT)
2435     {
2436       int last_anim_random_frame = gfx.anim_random_frame;
2437       int element = gpc->value;
2438       int graphic = el2panelimg(element);
2439
2440       if (gpc->value != gpc->last_value)
2441       {
2442         gpc->gfx_frame = 0;
2443         gpc->gfx_random = INIT_GFX_RANDOM();
2444       }
2445       else
2446       {
2447         gpc->gfx_frame++;
2448
2449         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2450             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452       }
2453
2454       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2455         gfx.anim_random_frame = gpc->gfx_random;
2456
2457       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2458         gpc->gfx_frame = element_info[element].collect_score;
2459
2460       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2461                                             gpc->gfx_frame);
2462
2463       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2464         gfx.anim_random_frame = last_anim_random_frame;
2465     }
2466   }
2467 }
2468
2469 void DisplayGameControlValues()
2470 {
2471   boolean redraw_panel = FALSE;
2472   int i;
2473
2474   for (i = 0; game_panel_controls[i].nr != -1; i++)
2475   {
2476     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2477
2478     if (PANEL_DEACTIVATED(gpc->pos))
2479       continue;
2480
2481     if (gpc->value == gpc->last_value &&
2482         gpc->frame == gpc->last_frame)
2483       continue;
2484
2485     redraw_panel = TRUE;
2486   }
2487
2488   if (!redraw_panel)
2489     return;
2490
2491   /* copy default game door content to main double buffer */
2492   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2493              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2494
2495   /* redraw game control buttons */
2496 #if 1
2497   RedrawGameButtons();
2498 #else
2499   UnmapGameButtons();
2500   MapGameButtons();
2501 #endif
2502
2503   game_status = GAME_MODE_PSEUDO_PANEL;
2504
2505 #if 1
2506   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2507 #else
2508   for (i = 0; game_panel_controls[i].nr != -1; i++)
2509 #endif
2510   {
2511 #if 1
2512     int nr = game_panel_order[i].nr;
2513     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2514 #else
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516     int nr = gpc->nr;
2517 #endif
2518     struct TextPosInfo *pos = gpc->pos;
2519     int type = gpc->type;
2520     int value = gpc->value;
2521     int frame = gpc->frame;
2522 #if 0
2523     int last_value = gpc->last_value;
2524     int last_frame = gpc->last_frame;
2525 #endif
2526     int size = pos->size;
2527     int font = pos->font;
2528     boolean draw_masked = pos->draw_masked;
2529     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2530
2531     if (PANEL_DEACTIVATED(pos))
2532       continue;
2533
2534 #if 0
2535     if (value == last_value && frame == last_frame)
2536       continue;
2537 #endif
2538
2539     gpc->last_value = value;
2540     gpc->last_frame = frame;
2541
2542 #if 0
2543     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2544 #endif
2545
2546     if (type == TYPE_INTEGER)
2547     {
2548       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2549           nr == GAME_PANEL_TIME)
2550       {
2551         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2552
2553         if (use_dynamic_size)           /* use dynamic number of digits */
2554         {
2555           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2556           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2557           int size2 = size1 + 1;
2558           int font1 = pos->font;
2559           int font2 = pos->font_alt;
2560
2561           size = (value < value_change ? size1 : size2);
2562           font = (value < value_change ? font1 : font2);
2563
2564 #if 0
2565           /* clear background if value just changed its size (dynamic digits) */
2566           if ((last_value < value_change) != (value < value_change))
2567           {
2568             int width1 = size1 * getFontWidth(font1);
2569             int width2 = size2 * getFontWidth(font2);
2570             int max_width = MAX(width1, width2);
2571             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2572
2573             pos->width = max_width;
2574
2575             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2576                                        max_width, max_height);
2577           }
2578 #endif
2579         }
2580       }
2581
2582 #if 1
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589 #endif
2590
2591       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2592                   int2str(value, size), font, mask_mode);
2593     }
2594     else if (type == TYPE_ELEMENT)
2595     {
2596       int element, graphic;
2597       Bitmap *src_bitmap;
2598       int src_x, src_y;
2599       int width, height;
2600       int dst_x = PANEL_XPOS(pos);
2601       int dst_y = PANEL_YPOS(pos);
2602
2603 #if 1
2604       if (value != EL_UNDEFINED && value != EL_EMPTY)
2605       {
2606         element = value;
2607         graphic = el2panelimg(value);
2608
2609         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2610
2611 #if 1
2612         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2613           size = TILESIZE;
2614 #endif
2615
2616         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2617                               &src_x, &src_y);
2618
2619         width  = graphic_info[graphic].width  * size / TILESIZE;
2620         height = graphic_info[graphic].height * size / TILESIZE;
2621
2622         if (draw_masked)
2623         {
2624           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2625                         dst_x - src_x, dst_y - src_y);
2626           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2627                            dst_x, dst_y);
2628         }
2629         else
2630         {
2631           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2632                      dst_x, dst_y);
2633         }
2634       }
2635 #else
2636       if (value == EL_UNDEFINED || value == EL_EMPTY)
2637       {
2638         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2639         graphic = el2panelimg(element);
2640
2641         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2642         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2643         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2644       }
2645       else
2646       {
2647         element = value;
2648         graphic = el2panelimg(value);
2649
2650         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2651       }
2652
2653       width  = graphic_info[graphic].width  * size / TILESIZE;
2654       height = graphic_info[graphic].height * size / TILESIZE;
2655
2656       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2657 #endif
2658     }
2659     else if (type == TYPE_STRING)
2660     {
2661       boolean active = (value != 0);
2662       char *state_normal = "off";
2663       char *state_active = "on";
2664       char *state = (active ? state_active : state_normal);
2665       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2666                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2667                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2668                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2669
2670       if (nr == GAME_PANEL_GRAVITY_STATE)
2671       {
2672         int font1 = pos->font;          /* (used for normal state) */
2673         int font2 = pos->font_alt;      /* (used for active state) */
2674 #if 0
2675         int size1 = strlen(state_normal);
2676         int size2 = strlen(state_active);
2677         int width1 = size1 * getFontWidth(font1);
2678         int width2 = size2 * getFontWidth(font2);
2679         int max_width = MAX(width1, width2);
2680         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2681
2682         pos->width = max_width;
2683
2684         /* clear background for values that may have changed its size */
2685         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2686                                    max_width, max_height);
2687 #endif
2688
2689         font = (active ? font2 : font1);
2690       }
2691
2692       if (s != NULL)
2693       {
2694         char *s_cut;
2695
2696 #if 1
2697         if (size <= 0)
2698         {
2699           /* don't truncate output if "chars" is zero or less */
2700           size = strlen(s);
2701
2702           /* dynamically correct text alignment */
2703           pos->width = size * getFontWidth(font);
2704         }
2705 #endif
2706
2707         s_cut = getStringCopyN(s, size);
2708
2709         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                     s_cut, font, mask_mode);
2711
2712         free(s_cut);
2713       }
2714     }
2715
2716     redraw_mask |= REDRAW_DOOR_1;
2717   }
2718
2719   game_status = GAME_MODE_PLAYING;
2720 }
2721
2722 void UpdateAndDisplayGameControlValues()
2723 {
2724   if (tape.warp_forward)
2725     return;
2726
2727   UpdateGameControlValues();
2728   DisplayGameControlValues();
2729 }
2730
2731 void DrawGameValue_Emeralds(int value)
2732 {
2733   struct TextPosInfo *pos = &game.panel.gems;
2734 #if 1
2735   int font_nr = pos->font;
2736 #else
2737   int font_nr = FONT_TEXT_2;
2738 #endif
2739   int font_width = getFontWidth(font_nr);
2740   int chars = pos->size;
2741
2742 #if 1
2743   return;       /* !!! USE NEW STUFF !!! */
2744 #endif
2745
2746   if (PANEL_DEACTIVATED(pos))
2747     return;
2748
2749   pos->width = chars * font_width;
2750
2751   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2752 }
2753
2754 void DrawGameValue_Dynamite(int value)
2755 {
2756   struct TextPosInfo *pos = &game.panel.inventory_count;
2757 #if 1
2758   int font_nr = pos->font;
2759 #else
2760   int font_nr = FONT_TEXT_2;
2761 #endif
2762   int font_width = getFontWidth(font_nr);
2763   int chars = pos->size;
2764
2765 #if 1
2766   return;       /* !!! USE NEW STUFF !!! */
2767 #endif
2768
2769   if (PANEL_DEACTIVATED(pos))
2770     return;
2771
2772   pos->width = chars * font_width;
2773
2774   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2775 }
2776
2777 void DrawGameValue_Score(int value)
2778 {
2779   struct TextPosInfo *pos = &game.panel.score;
2780 #if 1
2781   int font_nr = pos->font;
2782 #else
2783   int font_nr = FONT_TEXT_2;
2784 #endif
2785   int font_width = getFontWidth(font_nr);
2786   int chars = pos->size;
2787
2788 #if 1
2789   return;       /* !!! USE NEW STUFF !!! */
2790 #endif
2791
2792   if (PANEL_DEACTIVATED(pos))
2793     return;
2794
2795   pos->width = chars * font_width;
2796
2797   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2798 }
2799
2800 void DrawGameValue_Time(int value)
2801 {
2802   struct TextPosInfo *pos = &game.panel.time;
2803   static int last_value = -1;
2804   int chars1 = 3;
2805   int chars2 = 4;
2806   int chars = pos->size;
2807 #if 1
2808   int font1_nr = pos->font;
2809   int font2_nr = pos->font_alt;
2810 #else
2811   int font1_nr = FONT_TEXT_2;
2812   int font2_nr = FONT_TEXT_1;
2813 #endif
2814   int font_nr = font1_nr;
2815   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2816
2817 #if 1
2818   return;       /* !!! USE NEW STUFF !!! */
2819 #endif
2820
2821   if (PANEL_DEACTIVATED(pos))
2822     return;
2823
2824   if (use_dynamic_chars)                /* use dynamic number of chars */
2825   {
2826     chars   = (value < 1000 ? chars1   : chars2);
2827     font_nr = (value < 1000 ? font1_nr : font2_nr);
2828   }
2829
2830   /* clear background if value just changed its size (dynamic chars only) */
2831   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2832   {
2833     int width1 = chars1 * getFontWidth(font1_nr);
2834     int width2 = chars2 * getFontWidth(font2_nr);
2835     int max_width = MAX(width1, width2);
2836     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2837
2838     pos->width = max_width;
2839
2840     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2841                                max_width, max_height);
2842   }
2843
2844   pos->width = chars * getFontWidth(font_nr);
2845
2846   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2847
2848   last_value = value;
2849 }
2850
2851 void DrawGameValue_Level(int value)
2852 {
2853   struct TextPosInfo *pos = &game.panel.level_number;
2854   int chars1 = 2;
2855   int chars2 = 3;
2856   int chars = pos->size;
2857 #if 1
2858   int font1_nr = pos->font;
2859   int font2_nr = pos->font_alt;
2860 #else
2861   int font1_nr = FONT_TEXT_2;
2862   int font2_nr = FONT_TEXT_1;
2863 #endif
2864   int font_nr = font1_nr;
2865   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866
2867 #if 1
2868   return;       /* !!! USE NEW STUFF !!! */
2869 #endif
2870
2871   if (PANEL_DEACTIVATED(pos))
2872     return;
2873
2874   if (use_dynamic_chars)                /* use dynamic number of chars */
2875   {
2876     chars   = (level_nr < 100 ? chars1   : chars2);
2877     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2878   }
2879
2880   pos->width = chars * getFontWidth(font_nr);
2881
2882   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2883 }
2884
2885 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2886 {
2887 #if 0
2888   struct TextPosInfo *pos = &game.panel.keys;
2889 #endif
2890 #if 0
2891   int base_key_graphic = EL_KEY_1;
2892 #endif
2893   int i;
2894
2895 #if 1
2896   return;       /* !!! USE NEW STUFF !!! */
2897 #endif
2898
2899 #if 0
2900   if (PANEL_DEACTIVATED(pos))
2901     return;
2902 #endif
2903
2904 #if 0
2905   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2906     base_key_graphic = EL_EM_KEY_1;
2907 #endif
2908
2909 #if 0
2910   pos->width = 4 * MINI_TILEX;
2911 #endif
2912
2913 #if 1
2914   for (i = 0; i < MAX_NUM_KEYS; i++)
2915 #else
2916   /* currently only 4 of 8 possible keys are displayed */
2917   for (i = 0; i < STD_NUM_KEYS; i++)
2918 #endif
2919   {
2920 #if 1
2921     struct TextPosInfo *pos = &game.panel.key[i];
2922 #endif
2923     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2924     int src_y = DOOR_GFX_PAGEY1 + 123;
2925 #if 1
2926     int dst_x = PANEL_XPOS(pos);
2927     int dst_y = PANEL_YPOS(pos);
2928 #else
2929     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2930     int dst_y = PANEL_YPOS(pos);
2931 #endif
2932
2933 #if 1
2934     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2935                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2936                    EL_KEY_1) + i;
2937     int graphic = el2edimg(element);
2938 #endif
2939
2940 #if 1
2941     if (PANEL_DEACTIVATED(pos))
2942       continue;
2943 #endif
2944
2945 #if 0
2946     /* masked blit with tiles from half-size scaled bitmap does not work yet
2947        (no mask bitmap created for these sizes after loading and scaling) --
2948        solution: load without creating mask, scale, then create final mask */
2949
2950     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2951                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2952
2953     if (key[i])
2954     {
2955 #if 0
2956       int graphic = el2edimg(base_key_graphic + i);
2957 #endif
2958       Bitmap *src_bitmap;
2959       int src_x, src_y;
2960
2961       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2962
2963       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2964                     dst_x - src_x, dst_y - src_y);
2965       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2966                        dst_x, dst_y);
2967     }
2968 #else
2969 #if 1
2970     if (key[i])
2971       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2972     else
2973       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2974                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2975 #else
2976     if (key[i])
2977       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2978     else
2979       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2980                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2981 #endif
2982 #endif
2983   }
2984 }
2985
2986 #else
2987
2988 void DrawGameValue_Emeralds(int value)
2989 {
2990   int font_nr = FONT_TEXT_2;
2991   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2992
2993   if (PANEL_DEACTIVATED(game.panel.gems))
2994     return;
2995
2996   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2997 }
2998
2999 void DrawGameValue_Dynamite(int value)
3000 {
3001   int font_nr = FONT_TEXT_2;
3002   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3003
3004   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3005     return;
3006
3007   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3008 }
3009
3010 void DrawGameValue_Score(int value)
3011 {
3012   int font_nr = FONT_TEXT_2;
3013   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3014
3015   if (PANEL_DEACTIVATED(game.panel.score))
3016     return;
3017
3018   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3019 }
3020
3021 void DrawGameValue_Time(int value)
3022 {
3023   int font1_nr = FONT_TEXT_2;
3024 #if 1
3025   int font2_nr = FONT_TEXT_1;
3026 #else
3027   int font2_nr = FONT_LEVEL_NUMBER;
3028 #endif
3029   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3030   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3031
3032   if (PANEL_DEACTIVATED(game.panel.time))
3033     return;
3034
3035   /* clear background if value just changed its size */
3036   if (value == 999 || value == 1000)
3037     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3038
3039   if (value < 1000)
3040     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3041   else
3042     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3043 }
3044
3045 void DrawGameValue_Level(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
3054   if (PANEL_DEACTIVATED(game.panel.level))
3055     return;
3056
3057   if (level_nr < 100)
3058     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3059   else
3060     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3061 }
3062
3063 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3064 {
3065   int base_key_graphic = EL_KEY_1;
3066   int i;
3067
3068   if (PANEL_DEACTIVATED(game.panel.keys))
3069     return;
3070
3071   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3072     base_key_graphic = EL_EM_KEY_1;
3073
3074   /* currently only 4 of 8 possible keys are displayed */
3075   for (i = 0; i < STD_NUM_KEYS; i++)
3076   {
3077     int x = XX_KEYS + i * MINI_TILEX;
3078     int y = YY_KEYS;
3079
3080     if (key[i])
3081       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3082     else
3083       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3084                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3085   }
3086 }
3087
3088 #endif
3089
3090 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3091                        int key_bits)
3092 {
3093   int key[MAX_NUM_KEYS];
3094   int i;
3095
3096   /* prevent EM engine from updating time/score values parallel to GameWon() */
3097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3098       local_player->LevelSolved)
3099     return;
3100
3101   for (i = 0; i < MAX_NUM_KEYS; i++)
3102     key[i] = key_bits & (1 << i);
3103
3104   DrawGameValue_Level(level_nr);
3105
3106   DrawGameValue_Emeralds(emeralds);
3107   DrawGameValue_Dynamite(dynamite);
3108   DrawGameValue_Score(score);
3109   DrawGameValue_Time(time);
3110
3111   DrawGameValue_Keys(key);
3112 }
3113
3114 void UpdateGameDoorValues()
3115 {
3116   UpdateGameControlValues();
3117 }
3118
3119 void DrawGameDoorValues()
3120 {
3121   DisplayGameControlValues();
3122 }
3123
3124 void DrawGameDoorValues_OLD()
3125 {
3126   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3127   int dynamite_value = 0;
3128   int score_value = (local_player->LevelSolved ? local_player->score_final :
3129                      local_player->score);
3130   int gems_value = local_player->gems_still_needed;
3131   int key_bits = 0;
3132   int i, j;
3133
3134   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3135   {
3136     DrawGameDoorValues_EM();
3137
3138     return;
3139   }
3140
3141   if (game.centered_player_nr == -1)
3142   {
3143     for (i = 0; i < MAX_PLAYERS; i++)
3144     {
3145       for (j = 0; j < MAX_NUM_KEYS; j++)
3146         if (stored_player[i].key[j])
3147           key_bits |= (1 << j);
3148
3149       dynamite_value += stored_player[i].inventory_size;
3150     }
3151   }
3152   else
3153   {
3154     int player_nr = game.centered_player_nr;
3155
3156     for (i = 0; i < MAX_NUM_KEYS; i++)
3157       if (stored_player[player_nr].key[i])
3158         key_bits |= (1 << i);
3159
3160     dynamite_value = stored_player[player_nr].inventory_size;
3161   }
3162
3163   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3164                     key_bits);
3165 }
3166
3167
3168 /*
3169   =============================================================================
3170   InitGameEngine()
3171   -----------------------------------------------------------------------------
3172   initialize game engine due to level / tape version number
3173   =============================================================================
3174 */
3175
3176 static void InitGameEngine()
3177 {
3178   int i, j, k, l, x, y;
3179
3180   /* set game engine from tape file when re-playing, else from level file */
3181   game.engine_version = (tape.playing ? tape.engine_version :
3182                          level.game_version);
3183
3184   /* ---------------------------------------------------------------------- */
3185   /* set flags for bugs and changes according to active game engine version */
3186   /* ---------------------------------------------------------------------- */
3187
3188   /*
3189     Summary of bugfix/change:
3190     Fixed handling for custom elements that change when pushed by the player.
3191
3192     Fixed/changed in version:
3193     3.1.0
3194
3195     Description:
3196     Before 3.1.0, custom elements that "change when pushing" changed directly
3197     after the player started pushing them (until then handled in "DigField()").
3198     Since 3.1.0, these custom elements are not changed until the "pushing"
3199     move of the element is finished (now handled in "ContinueMoving()").
3200
3201     Affected levels/tapes:
3202     The first condition is generally needed for all levels/tapes before version
3203     3.1.0, which might use the old behaviour before it was changed; known tapes
3204     that are affected are some tapes from the level set "Walpurgis Gardens" by
3205     Jamie Cullen.
3206     The second condition is an exception from the above case and is needed for
3207     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3208     above (including some development versions of 3.1.0), but before it was
3209     known that this change would break tapes like the above and was fixed in
3210     3.1.1, so that the changed behaviour was active although the engine version
3211     while recording maybe was before 3.1.0. There is at least one tape that is
3212     affected by this exception, which is the tape for the one-level set "Bug
3213     Machine" by Juergen Bonhagen.
3214   */
3215
3216   game.use_change_when_pushing_bug =
3217     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3218      !(tape.playing &&
3219        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3220        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3221
3222   /*
3223     Summary of bugfix/change:
3224     Fixed handling for blocking the field the player leaves when moving.
3225
3226     Fixed/changed in version:
3227     3.1.1
3228
3229     Description:
3230     Before 3.1.1, when "block last field when moving" was enabled, the field
3231     the player is leaving when moving was blocked for the time of the move,
3232     and was directly unblocked afterwards. This resulted in the last field
3233     being blocked for exactly one less than the number of frames of one player
3234     move. Additionally, even when blocking was disabled, the last field was
3235     blocked for exactly one frame.
3236     Since 3.1.1, due to changes in player movement handling, the last field
3237     is not blocked at all when blocking is disabled. When blocking is enabled,
3238     the last field is blocked for exactly the number of frames of one player
3239     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3240     last field is blocked for exactly one more than the number of frames of
3241     one player move.
3242
3243     Affected levels/tapes:
3244     (!!! yet to be determined -- probably many !!!)
3245   */
3246
3247   game.use_block_last_field_bug =
3248     (game.engine_version < VERSION_IDENT(3,1,1,0));
3249
3250   /*
3251     Summary of bugfix/change:
3252     Changed behaviour of CE changes with multiple changes per single frame.
3253
3254     Fixed/changed in version:
3255     3.2.0-6
3256
3257     Description:
3258     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3259     This resulted in race conditions where CEs seem to behave strange in some
3260     situations (where triggered CE changes were just skipped because there was
3261     already a CE change on that tile in the playfield in that engine frame).
3262     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3263     (The number of changes per frame must be limited in any case, because else
3264     it is easily possible to define CE changes that would result in an infinite
3265     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3266     should be set large enough so that it would only be reached in cases where
3267     the corresponding CE change conditions run into a loop. Therefore, it seems
3268     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3269     maximal number of change pages for custom elements.)
3270
3271     Affected levels/tapes:
3272     Probably many.
3273   */
3274
3275 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3276   game.max_num_changes_per_frame = 1;
3277 #else
3278   game.max_num_changes_per_frame =
3279     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3280 #endif
3281
3282   /* ---------------------------------------------------------------------- */
3283
3284   /* default scan direction: scan playfield from top/left to bottom/right */
3285   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3286
3287   /* dynamically adjust element properties according to game engine version */
3288   InitElementPropertiesEngine(game.engine_version);
3289
3290 #if 0
3291   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3292   printf("          tape version == %06d [%s] [file: %06d]\n",
3293          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3294          tape.file_version);
3295   printf("       => game.engine_version == %06d\n", game.engine_version);
3296 #endif
3297
3298   /* ---------- initialize player's initial move delay --------------------- */
3299
3300   /* dynamically adjust player properties according to level information */
3301   for (i = 0; i < MAX_PLAYERS; i++)
3302     game.initial_move_delay_value[i] =
3303       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3304
3305   /* dynamically adjust player properties according to game engine version */
3306   for (i = 0; i < MAX_PLAYERS; i++)
3307     game.initial_move_delay[i] =
3308       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3309        game.initial_move_delay_value[i] : 0);
3310
3311   /* ---------- initialize player's initial push delay --------------------- */
3312
3313   /* dynamically adjust player properties according to game engine version */
3314   game.initial_push_delay_value =
3315     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3316
3317   /* ---------- initialize changing elements ------------------------------- */
3318
3319   /* initialize changing elements information */
3320   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3321   {
3322     struct ElementInfo *ei = &element_info[i];
3323
3324     /* this pointer might have been changed in the level editor */
3325     ei->change = &ei->change_page[0];
3326
3327     if (!IS_CUSTOM_ELEMENT(i))
3328     {
3329       ei->change->target_element = EL_EMPTY_SPACE;
3330       ei->change->delay_fixed = 0;
3331       ei->change->delay_random = 0;
3332       ei->change->delay_frames = 1;
3333     }
3334
3335     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3336     {
3337       ei->has_change_event[j] = FALSE;
3338
3339       ei->event_page_nr[j] = 0;
3340       ei->event_page[j] = &ei->change_page[0];
3341     }
3342   }
3343
3344   /* add changing elements from pre-defined list */
3345   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3346   {
3347     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3348     struct ElementInfo *ei = &element_info[ch_delay->element];
3349
3350     ei->change->target_element       = ch_delay->target_element;
3351     ei->change->delay_fixed          = ch_delay->change_delay;
3352
3353     ei->change->pre_change_function  = ch_delay->pre_change_function;
3354     ei->change->change_function      = ch_delay->change_function;
3355     ei->change->post_change_function = ch_delay->post_change_function;
3356
3357     ei->change->can_change = TRUE;
3358     ei->change->can_change_or_has_action = TRUE;
3359
3360     ei->has_change_event[CE_DELAY] = TRUE;
3361
3362     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3363     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3364   }
3365
3366   /* ---------- initialize internal run-time variables --------------------- */
3367
3368   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3369   {
3370     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3371
3372     for (j = 0; j < ei->num_change_pages; j++)
3373     {
3374       ei->change_page[j].can_change_or_has_action =
3375         (ei->change_page[j].can_change |
3376          ei->change_page[j].has_action);
3377     }
3378   }
3379
3380   /* add change events from custom element configuration */
3381   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3382   {
3383     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3384
3385     for (j = 0; j < ei->num_change_pages; j++)
3386     {
3387       if (!ei->change_page[j].can_change_or_has_action)
3388         continue;
3389
3390       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3391       {
3392         /* only add event page for the first page found with this event */
3393         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3394         {
3395           ei->has_change_event[k] = TRUE;
3396
3397           ei->event_page_nr[k] = j;
3398           ei->event_page[k] = &ei->change_page[j];
3399         }
3400       }
3401     }
3402   }
3403
3404 #if 1
3405   /* ---------- initialize reference elements in change conditions --------- */
3406
3407   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3408   {
3409     int element = EL_CUSTOM_START + i;
3410     struct ElementInfo *ei = &element_info[element];
3411
3412     for (j = 0; j < ei->num_change_pages; j++)
3413     {
3414       int trigger_element = ei->change_page[j].initial_trigger_element;
3415
3416       if (trigger_element >= EL_PREV_CE_8 &&
3417           trigger_element <= EL_NEXT_CE_8)
3418         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3419
3420       ei->change_page[j].trigger_element = trigger_element;
3421     }
3422   }
3423 #endif
3424
3425   /* ---------- initialize run-time trigger player and element ------------- */
3426
3427   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3428   {
3429     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3430
3431     for (j = 0; j < ei->num_change_pages; j++)
3432     {
3433       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3434       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3435       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3436       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3437       ei->change_page[j].actual_trigger_ce_value = 0;
3438       ei->change_page[j].actual_trigger_ce_score = 0;
3439     }
3440   }
3441
3442   /* ---------- initialize trigger events ---------------------------------- */
3443
3444   /* initialize trigger events information */
3445   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3446     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3447       trigger_events[i][j] = FALSE;
3448
3449   /* add trigger events from element change event properties */
3450   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3451   {
3452     struct ElementInfo *ei = &element_info[i];
3453
3454     for (j = 0; j < ei->num_change_pages; j++)
3455     {
3456       if (!ei->change_page[j].can_change_or_has_action)
3457         continue;
3458
3459       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3460       {
3461         int trigger_element = ei->change_page[j].trigger_element;
3462
3463         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3464         {
3465           if (ei->change_page[j].has_event[k])
3466           {
3467             if (IS_GROUP_ELEMENT(trigger_element))
3468             {
3469               struct ElementGroupInfo *group =
3470                 element_info[trigger_element].group;
3471
3472               for (l = 0; l < group->num_elements_resolved; l++)
3473                 trigger_events[group->element_resolved[l]][k] = TRUE;
3474             }
3475             else if (trigger_element == EL_ANY_ELEMENT)
3476               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3477                 trigger_events[l][k] = TRUE;
3478             else
3479               trigger_events[trigger_element][k] = TRUE;
3480           }
3481         }
3482       }
3483     }
3484   }
3485
3486   /* ---------- initialize push delay -------------------------------------- */
3487
3488   /* initialize push delay values to default */
3489   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3490   {
3491     if (!IS_CUSTOM_ELEMENT(i))
3492     {
3493       /* set default push delay values (corrected since version 3.0.7-1) */
3494       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3495       {
3496         element_info[i].push_delay_fixed = 2;
3497         element_info[i].push_delay_random = 8;
3498       }
3499       else
3500       {
3501         element_info[i].push_delay_fixed = 8;
3502         element_info[i].push_delay_random = 8;
3503       }
3504     }
3505   }
3506
3507   /* set push delay value for certain elements from pre-defined list */
3508   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3509   {
3510     int e = push_delay_list[i].element;
3511
3512     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3513     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3514   }
3515
3516   /* set push delay value for Supaplex elements for newer engine versions */
3517   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3518   {
3519     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3520     {
3521       if (IS_SP_ELEMENT(i))
3522       {
3523         /* set SP push delay to just enough to push under a falling zonk */
3524         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3525
3526         element_info[i].push_delay_fixed  = delay;
3527         element_info[i].push_delay_random = 0;
3528       }
3529     }
3530   }
3531
3532   /* ---------- initialize move stepsize ----------------------------------- */
3533
3534   /* initialize move stepsize values to default */
3535   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3536     if (!IS_CUSTOM_ELEMENT(i))
3537       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3538
3539   /* set move stepsize value for certain elements from pre-defined list */
3540   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3541   {
3542     int e = move_stepsize_list[i].element;
3543
3544     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3545   }
3546
3547   /* ---------- initialize collect score ----------------------------------- */
3548
3549   /* initialize collect score values for custom elements from initial value */
3550   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3551     if (IS_CUSTOM_ELEMENT(i))
3552       element_info[i].collect_score = element_info[i].collect_score_initial;
3553
3554   /* ---------- initialize collect count ----------------------------------- */
3555
3556   /* initialize collect count values for non-custom elements */
3557   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558     if (!IS_CUSTOM_ELEMENT(i))
3559       element_info[i].collect_count_initial = 0;
3560
3561   /* add collect count values for all elements from pre-defined list */
3562   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3563     element_info[collect_count_list[i].element].collect_count_initial =
3564       collect_count_list[i].count;
3565
3566   /* ---------- initialize access direction -------------------------------- */
3567
3568   /* initialize access direction values to default (access from every side) */
3569   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570     if (!IS_CUSTOM_ELEMENT(i))
3571       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3572
3573   /* set access direction value for certain elements from pre-defined list */
3574   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3575     element_info[access_direction_list[i].element].access_direction =
3576       access_direction_list[i].direction;
3577
3578   /* ---------- initialize explosion content ------------------------------- */
3579   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3580   {
3581     if (IS_CUSTOM_ELEMENT(i))
3582       continue;
3583
3584     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3585     {
3586       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3587
3588       element_info[i].content.e[x][y] =
3589         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3590          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3591          i == EL_PLAYER_3 ? EL_EMERALD :
3592          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3593          i == EL_MOLE ? EL_EMERALD_RED :
3594          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3595          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3596          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3597          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3598          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3599          i == EL_WALL_EMERALD ? EL_EMERALD :
3600          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3601          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3602          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3603          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3604          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3605          i == EL_WALL_PEARL ? EL_PEARL :
3606          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3607          EL_EMPTY);
3608     }
3609   }
3610
3611   /* ---------- initialize recursion detection ------------------------------ */
3612   recursion_loop_depth = 0;
3613   recursion_loop_detected = FALSE;
3614   recursion_loop_element = EL_UNDEFINED;
3615
3616   /* ---------- initialize graphics engine ---------------------------------- */
3617   game.scroll_delay_value =
3618     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3619      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3620   game.scroll_delay_value =
3621     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3622 }
3623
3624 int get_num_special_action(int element, int action_first, int action_last)
3625 {
3626   int num_special_action = 0;
3627   int i, j;
3628
3629   for (i = action_first; i <= action_last; i++)
3630   {
3631     boolean found = FALSE;
3632
3633     for (j = 0; j < NUM_DIRECTIONS; j++)
3634       if (el_act_dir2img(element, i, j) !=
3635           el_act_dir2img(element, ACTION_DEFAULT, j))
3636         found = TRUE;
3637
3638     if (found)
3639       num_special_action++;
3640     else
3641       break;
3642   }
3643
3644   return num_special_action;
3645 }
3646
3647
3648 /*
3649   =============================================================================
3650   InitGame()
3651   -----------------------------------------------------------------------------
3652   initialize and start new game
3653   =============================================================================
3654 */
3655
3656 void InitGame()
3657 {
3658   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3659   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3660   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3661 #if 0
3662   boolean do_fading = (game_status == GAME_MODE_MAIN);
3663 #endif
3664   int i, j, x, y;
3665
3666   game_status = GAME_MODE_PLAYING;
3667
3668   InitGameEngine();
3669   InitGameControlValues();
3670
3671   /* don't play tapes over network */
3672   network_playing = (options.network && !tape.playing);
3673
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     struct PlayerInfo *player = &stored_player[i];
3677
3678     player->index_nr = i;
3679     player->index_bit = (1 << i);
3680     player->element_nr = EL_PLAYER_1 + i;
3681
3682     player->present = FALSE;
3683     player->active = FALSE;
3684     player->killed = FALSE;
3685
3686     player->action = 0;
3687     player->effective_action = 0;
3688     player->programmed_action = 0;
3689
3690     player->score = 0;
3691     player->score_final = 0;
3692
3693     player->gems_still_needed = level.gems_needed;
3694     player->sokobanfields_still_needed = 0;
3695     player->lights_still_needed = 0;
3696     player->friends_still_needed = 0;
3697
3698     for (j = 0; j < MAX_NUM_KEYS; j++)
3699       player->key[j] = FALSE;
3700
3701     player->num_white_keys = 0;
3702
3703     player->dynabomb_count = 0;
3704     player->dynabomb_size = 1;
3705     player->dynabombs_left = 0;
3706     player->dynabomb_xl = FALSE;
3707
3708     player->MovDir = MV_NONE;
3709     player->MovPos = 0;
3710     player->GfxPos = 0;
3711     player->GfxDir = MV_NONE;
3712     player->GfxAction = ACTION_DEFAULT;
3713     player->Frame = 0;
3714     player->StepFrame = 0;
3715
3716     player->use_murphy = FALSE;
3717     player->artwork_element =
3718       (level.use_artwork_element[i] ? level.artwork_element[i] :
3719        player->element_nr);
3720
3721     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3722     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3723
3724     player->gravity = level.initial_player_gravity[i];
3725
3726     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3727
3728     player->actual_frame_counter = 0;
3729
3730     player->step_counter = 0;
3731
3732     player->last_move_dir = MV_NONE;
3733
3734     player->is_active = FALSE;
3735
3736     player->is_waiting = FALSE;
3737     player->is_moving = FALSE;
3738     player->is_auto_moving = FALSE;
3739     player->is_digging = FALSE;
3740     player->is_snapping = FALSE;
3741     player->is_collecting = FALSE;
3742     player->is_pushing = FALSE;
3743     player->is_switching = FALSE;
3744     player->is_dropping = FALSE;
3745     player->is_dropping_pressed = FALSE;
3746
3747     player->is_bored = FALSE;
3748     player->is_sleeping = FALSE;
3749
3750     player->frame_counter_bored = -1;
3751     player->frame_counter_sleeping = -1;
3752
3753     player->anim_delay_counter = 0;
3754     player->post_delay_counter = 0;
3755
3756     player->dir_waiting = MV_NONE;
3757     player->action_waiting = ACTION_DEFAULT;
3758     player->last_action_waiting = ACTION_DEFAULT;
3759     player->special_action_bored = ACTION_DEFAULT;
3760     player->special_action_sleeping = ACTION_DEFAULT;
3761
3762     player->switch_x = -1;
3763     player->switch_y = -1;
3764
3765     player->drop_x = -1;
3766     player->drop_y = -1;
3767
3768     player->show_envelope = 0;
3769
3770     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3771
3772     player->push_delay       = -1;      /* initialized when pushing starts */
3773     player->push_delay_value = game.initial_push_delay_value;
3774
3775     player->drop_delay = 0;
3776     player->drop_pressed_delay = 0;
3777
3778     player->last_jx = -1;
3779     player->last_jy = -1;
3780     player->jx = -1;
3781     player->jy = -1;
3782
3783     player->shield_normal_time_left = 0;
3784     player->shield_deadly_time_left = 0;
3785
3786     player->inventory_infinite_element = EL_UNDEFINED;
3787     player->inventory_size = 0;
3788
3789     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3790     SnapField(player, 0, 0);
3791
3792     player->LevelSolved = FALSE;
3793     player->GameOver = FALSE;
3794
3795     player->LevelSolved_GameWon = FALSE;
3796     player->LevelSolved_GameEnd = FALSE;
3797     player->LevelSolved_PanelOff = FALSE;
3798     player->LevelSolved_SaveTape = FALSE;
3799     player->LevelSolved_SaveScore = FALSE;
3800     player->LevelSolved_CountingTime = 0;
3801     player->LevelSolved_CountingScore = 0;
3802   }
3803
3804   network_player_action_received = FALSE;
3805
3806 #if defined(NETWORK_AVALIABLE)
3807   /* initial null action */
3808   if (network_playing)
3809     SendToServer_MovePlayer(MV_NONE);
3810 #endif
3811
3812   ZX = ZY = -1;
3813   ExitX = ExitY = -1;
3814
3815   FrameCounter = 0;
3816   TimeFrames = 0;
3817   TimePlayed = 0;
3818   TimeLeft = level.time;
3819   TapeTime = 0;
3820
3821   ScreenMovDir = MV_NONE;
3822   ScreenMovPos = 0;
3823   ScreenGfxPos = 0;
3824
3825   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3826
3827   AllPlayersGone = FALSE;
3828
3829   game.yamyam_content_nr = 0;
3830   game.robot_wheel_active = FALSE;
3831   game.magic_wall_active = FALSE;
3832   game.magic_wall_time_left = 0;
3833   game.light_time_left = 0;
3834   game.timegate_time_left = 0;
3835   game.switchgate_pos = 0;
3836   game.wind_direction = level.wind_direction_initial;
3837
3838 #if !USE_PLAYER_GRAVITY
3839   game.gravity = FALSE;
3840   game.explosions_delayed = TRUE;
3841 #endif
3842
3843   game.lenses_time_left = 0;
3844   game.magnify_time_left = 0;
3845
3846   game.ball_state = level.ball_state_initial;
3847   game.ball_content_nr = 0;
3848
3849   game.envelope_active = FALSE;
3850
3851   /* set focus to local player for network games, else to all players */
3852   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3853   game.centered_player_nr_next = game.centered_player_nr;
3854   game.set_centered_player = FALSE;
3855
3856   if (network_playing && tape.recording)
3857   {
3858     /* store client dependent player focus when recording network games */
3859     tape.centered_player_nr_next = game.centered_player_nr_next;
3860     tape.set_centered_player = TRUE;
3861   }
3862
3863   for (i = 0; i < NUM_BELTS; i++)
3864   {
3865     game.belt_dir[i] = MV_NONE;
3866     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3867   }
3868
3869   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3870     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3871
3872   SCAN_PLAYFIELD(x, y)
3873   {
3874     Feld[x][y] = level.field[x][y];
3875     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3876     ChangeDelay[x][y] = 0;
3877     ChangePage[x][y] = -1;
3878 #if USE_NEW_CUSTOM_VALUE
3879     CustomValue[x][y] = 0;              /* initialized in InitField() */
3880 #endif
3881     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3882     AmoebaNr[x][y] = 0;
3883     WasJustMoving[x][y] = 0;
3884     WasJustFalling[x][y] = 0;
3885     CheckCollision[x][y] = 0;
3886     CheckImpact[x][y] = 0;
3887     Stop[x][y] = FALSE;
3888     Pushed[x][y] = FALSE;
3889
3890     ChangeCount[x][y] = 0;
3891     ChangeEvent[x][y] = -1;
3892
3893     ExplodePhase[x][y] = 0;
3894     ExplodeDelay[x][y] = 0;
3895     ExplodeField[x][y] = EX_TYPE_NONE;
3896
3897     RunnerVisit[x][y] = 0;
3898     PlayerVisit[x][y] = 0;
3899
3900     GfxFrame[x][y] = 0;
3901     GfxRandom[x][y] = INIT_GFX_RANDOM();
3902     GfxElement[x][y] = EL_UNDEFINED;
3903     GfxAction[x][y] = ACTION_DEFAULT;
3904     GfxDir[x][y] = MV_NONE;
3905     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3906   }
3907
3908   SCAN_PLAYFIELD(x, y)
3909   {
3910     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3911       emulate_bd = FALSE;
3912     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3913       emulate_sb = FALSE;
3914     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3915       emulate_sp = FALSE;
3916
3917     InitField(x, y, TRUE);
3918
3919     ResetGfxAnimation(x, y);
3920   }
3921
3922   InitBeltMovement();
3923
3924   for (i = 0; i < MAX_PLAYERS; i++)
3925   {
3926     struct PlayerInfo *player = &stored_player[i];
3927
3928     /* set number of special actions for bored and sleeping animation */
3929     player->num_special_action_bored =
3930       get_num_special_action(player->artwork_element,
3931                              ACTION_BORING_1, ACTION_BORING_LAST);
3932     player->num_special_action_sleeping =
3933       get_num_special_action(player->artwork_element,
3934                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3935   }
3936
3937   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3938                     emulate_sb ? EMU_SOKOBAN :
3939                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3940
3941 #if USE_NEW_ALL_SLIPPERY
3942   /* initialize type of slippery elements */
3943   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3944   {
3945     if (!IS_CUSTOM_ELEMENT(i))
3946     {
3947       /* default: elements slip down either to the left or right randomly */
3948       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3949
3950       /* SP style elements prefer to slip down on the left side */
3951       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3952         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3953
3954       /* BD style elements prefer to slip down on the left side */
3955       if (game.emulation == EMU_BOULDERDASH)
3956         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3957     }
3958   }
3959 #endif
3960
3961   /* initialize explosion and ignition delay */
3962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3963   {
3964     if (!IS_CUSTOM_ELEMENT(i))
3965     {
3966       int num_phase = 8;
3967       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3968                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3969                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3970       int last_phase = (num_phase + 1) * delay;
3971       int half_phase = (num_phase / 2) * delay;
3972
3973       element_info[i].explosion_delay = last_phase - 1;
3974       element_info[i].ignition_delay = half_phase;
3975
3976       if (i == EL_BLACK_ORB)
3977         element_info[i].ignition_delay = 1;
3978     }
3979
3980 #if 0
3981     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3982       element_info[i].explosion_delay = 1;
3983
3984     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3985       element_info[i].ignition_delay = 1;
3986 #endif
3987   }
3988
3989   /* correct non-moving belts to start moving left */
3990   for (i = 0; i < NUM_BELTS; i++)
3991     if (game.belt_dir[i] == MV_NONE)
3992       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3993
3994   /* check if any connected player was not found in playfield */
3995   for (i = 0; i < MAX_PLAYERS; i++)
3996   {
3997     struct PlayerInfo *player = &stored_player[i];
3998
3999     if (player->connected && !player->present)
4000     {
4001       for (j = 0; j < MAX_PLAYERS; j++)
4002       {
4003         struct PlayerInfo *some_player = &stored_player[j];
4004         int jx = some_player->jx, jy = some_player->jy;
4005
4006         /* assign first free player found that is present in the playfield */
4007         if (some_player->present && !some_player->connected)
4008         {
4009           player->present = TRUE;
4010           player->active = TRUE;
4011
4012           some_player->present = FALSE;
4013           some_player->active = FALSE;
4014
4015           player->artwork_element = some_player->artwork_element;
4016
4017           player->block_last_field       = some_player->block_last_field;
4018           player->block_delay_adjustment = some_player->block_delay_adjustment;
4019
4020           StorePlayer[jx][jy] = player->element_nr;
4021           player->jx = player->last_jx = jx;
4022           player->jy = player->last_jy = jy;
4023
4024           break;
4025         }
4026       }
4027     }
4028   }
4029
4030   if (tape.playing)
4031   {
4032     /* when playing a tape, eliminate all players who do not participate */
4033
4034     for (i = 0; i < MAX_PLAYERS; i++)
4035     {
4036       if (stored_player[i].active && !tape.player_participates[i])
4037       {
4038         struct PlayerInfo *player = &stored_player[i];
4039         int jx = player->jx, jy = player->jy;
4040
4041         player->active = FALSE;
4042         StorePlayer[jx][jy] = 0;
4043         Feld[jx][jy] = EL_EMPTY;
4044       }
4045     }
4046   }
4047   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4048   {
4049     /* when in single player mode, eliminate all but the first active player */
4050
4051     for (i = 0; i < MAX_PLAYERS; i++)
4052     {
4053       if (stored_player[i].active)
4054       {
4055         for (j = i + 1; j < MAX_PLAYERS; j++)
4056         {
4057           if (stored_player[j].active)
4058           {
4059             struct PlayerInfo *player = &stored_player[j];
4060             int jx = player->jx, jy = player->jy;
4061
4062             player->active = FALSE;
4063             player->present = FALSE;
4064
4065             StorePlayer[jx][jy] = 0;
4066             Feld[jx][jy] = EL_EMPTY;
4067           }
4068         }
4069       }
4070     }
4071   }
4072
4073   /* when recording the game, store which players take part in the game */
4074   if (tape.recording)
4075   {
4076     for (i = 0; i < MAX_PLAYERS; i++)
4077       if (stored_player[i].active)
4078         tape.player_participates[i] = TRUE;
4079   }
4080
4081   if (options.debug)
4082   {
4083     for (i = 0; i < MAX_PLAYERS; i++)
4084     {
4085       struct PlayerInfo *player = &stored_player[i];
4086
4087       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4088              i+1,
4089              player->present,
4090              player->connected,
4091              player->active);
4092       if (local_player == player)
4093         printf("Player  %d is local player.\n", i+1);
4094     }
4095   }
4096
4097   if (BorderElement == EL_EMPTY)
4098   {
4099     SBX_Left = 0;
4100     SBX_Right = lev_fieldx - SCR_FIELDX;
4101     SBY_Upper = 0;
4102     SBY_Lower = lev_fieldy - SCR_FIELDY;
4103   }
4104   else
4105   {
4106     SBX_Left = -1;
4107     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4108     SBY_Upper = -1;
4109     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4110   }
4111
4112   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4113     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4114
4115   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4116     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4117
4118   /* if local player not found, look for custom element that might create
4119      the player (make some assumptions about the right custom element) */
4120   if (!local_player->present)
4121   {
4122     int start_x = 0, start_y = 0;
4123     int found_rating = 0;
4124     int found_element = EL_UNDEFINED;
4125     int player_nr = local_player->index_nr;
4126
4127     SCAN_PLAYFIELD(x, y)
4128     {
4129       int element = Feld[x][y];
4130       int content;
4131       int xx, yy;
4132       boolean is_player;
4133
4134       if (level.use_start_element[player_nr] &&
4135           level.start_element[player_nr] == element &&
4136           found_rating < 4)
4137       {
4138         start_x = x;
4139         start_y = y;
4140
4141         found_rating = 4;
4142         found_element = element;
4143       }
4144
4145       if (!IS_CUSTOM_ELEMENT(element))
4146         continue;
4147
4148       if (CAN_CHANGE(element))
4149       {
4150         for (i = 0; i < element_info[element].num_change_pages; i++)
4151         {
4152           /* check for player created from custom element as single target */
4153           content = element_info[element].change_page[i].target_element;
4154           is_player = ELEM_IS_PLAYER(content);
4155
4156           if (is_player && (found_rating < 3 ||
4157                             (found_rating == 3 && element < found_element)))
4158           {
4159             start_x = x;
4160             start_y = y;
4161
4162             found_rating = 3;
4163             found_element = element;
4164           }
4165         }
4166       }
4167
4168       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4169       {
4170         /* check for player created from custom element as explosion content */
4171         content = element_info[element].content.e[xx][yy];
4172         is_player = ELEM_IS_PLAYER(content);
4173
4174         if (is_player && (found_rating < 2 ||
4175                           (found_rating == 2 && element < found_element)))
4176         {
4177           start_x = x + xx - 1;
4178           start_y = y + yy - 1;
4179
4180           found_rating = 2;
4181           found_element = element;
4182         }
4183
4184         if (!CAN_CHANGE(element))
4185           continue;
4186
4187         for (i = 0; i < element_info[element].num_change_pages; i++)
4188         {
4189           /* check for player created from custom element as extended target */
4190           content =
4191             element_info[element].change_page[i].target_content.e[xx][yy];
4192
4193           is_player = ELEM_IS_PLAYER(content);
4194
4195           if (is_player && (found_rating < 1 ||
4196                             (found_rating == 1 && element < found_element)))
4197           {
4198             start_x = x + xx - 1;
4199             start_y = y + yy - 1;
4200
4201             found_rating = 1;
4202             found_element = element;
4203           }
4204         }
4205       }
4206     }
4207
4208     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4209                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4210                 start_x - MIDPOSX);
4211
4212     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4213                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4214                 start_y - MIDPOSY);
4215   }
4216   else
4217   {
4218     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4219                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4220                 local_player->jx - MIDPOSX);
4221
4222     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4223                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4224                 local_player->jy - MIDPOSY);
4225   }
4226
4227 #if 0
4228   /* do not use PLAYING mask for fading out from main screen */
4229   game_status = GAME_MODE_MAIN;
4230 #endif
4231
4232   StopAnimation();
4233
4234   if (!game.restart_level)
4235     CloseDoor(DOOR_CLOSE_1);
4236
4237 #if 1
4238   if (level_editor_test_game)
4239     FadeSkipNextFadeIn();
4240   else
4241     FadeSetEnterScreen();
4242 #else
4243   if (level_editor_test_game)
4244     fading = fading_none;
4245   else
4246     fading = menu.destination;
4247 #endif
4248
4249 #if 1
4250   FadeOut(REDRAW_FIELD);
4251 #else
4252   if (do_fading)
4253     FadeOut(REDRAW_FIELD);
4254 #endif
4255
4256 #if 0
4257   game_status = GAME_MODE_PLAYING;
4258 #endif
4259
4260   /* !!! FIX THIS (START) !!! */
4261   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4262   {
4263     InitGameEngine_EM();
4264
4265     /* blit playfield from scroll buffer to normal back buffer for fading in */
4266     BlitScreenToBitmap_EM(backbuffer);
4267   }
4268   else
4269   {
4270     DrawLevel();
4271     DrawAllPlayers();
4272
4273     /* after drawing the level, correct some elements */
4274     if (game.timegate_time_left == 0)
4275       CloseAllOpenTimegates();
4276
4277     /* blit playfield from scroll buffer to normal back buffer for fading in */
4278     if (setup.soft_scrolling)
4279       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4280
4281     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4282   }
4283   /* !!! FIX THIS (END) !!! */
4284
4285 #if 1
4286   FadeIn(REDRAW_FIELD);
4287 #else
4288   if (do_fading)
4289     FadeIn(REDRAW_FIELD);
4290
4291   BackToFront();
4292 #endif
4293
4294   if (!game.restart_level)
4295   {
4296     /* copy default game door content to main double buffer */
4297     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4298                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4299   }
4300
4301   SetPanelBackground();
4302   SetDrawBackgroundMask(REDRAW_DOOR_1);
4303
4304 #if 1
4305   UpdateAndDisplayGameControlValues();
4306 #else
4307   UpdateGameDoorValues();
4308   DrawGameDoorValues();
4309 #endif
4310
4311   if (!game.restart_level)
4312   {
4313     UnmapGameButtons();
4314     UnmapTapeButtons();
4315     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4316     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4317     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4318     MapGameButtons();
4319     MapTapeButtons();
4320
4321     /* copy actual game door content to door double buffer for OpenDoor() */
4322     BlitBitmap(drawto, bitmap_db_door,
4323                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4324
4325     OpenDoor(DOOR_OPEN_ALL);
4326
4327     PlaySound(SND_GAME_STARTING);
4328
4329     if (setup.sound_music)
4330       PlayLevelMusic();
4331
4332     KeyboardAutoRepeatOffUnlessAutoplay();
4333
4334     if (options.debug)
4335     {
4336       for (i = 0; i < MAX_PLAYERS; i++)
4337         printf("Player %d %sactive.\n",
4338                i + 1, (stored_player[i].active ? "" : "not "));
4339     }
4340   }
4341
4342 #if 1
4343   UnmapAllGadgets();
4344
4345   MapGameButtons();
4346   MapTapeButtons();
4347 #endif
4348
4349   game.restart_level = FALSE;
4350 }
4351
4352 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4353 {
4354   /* this is used for non-R'n'D game engines to update certain engine values */
4355
4356   /* needed to determine if sounds are played within the visible screen area */
4357   scroll_x = actual_scroll_x;
4358   scroll_y = actual_scroll_y;
4359 }
4360
4361 void InitMovDir(int x, int y)
4362 {
4363   int i, element = Feld[x][y];
4364   static int xy[4][2] =
4365   {
4366     {  0, +1 },
4367     { +1,  0 },
4368     {  0, -1 },
4369     { -1,  0 }
4370   };
4371   static int direction[3][4] =
4372   {
4373     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4374     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4375     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4376   };
4377
4378   switch (element)
4379   {
4380     case EL_BUG_RIGHT:
4381     case EL_BUG_UP:
4382     case EL_BUG_LEFT:
4383     case EL_BUG_DOWN:
4384       Feld[x][y] = EL_BUG;
4385       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4386       break;
4387
4388     case EL_SPACESHIP_RIGHT:
4389     case EL_SPACESHIP_UP:
4390     case EL_SPACESHIP_LEFT:
4391     case EL_SPACESHIP_DOWN:
4392       Feld[x][y] = EL_SPACESHIP;
4393       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4394       break;
4395
4396     case EL_BD_BUTTERFLY_RIGHT:
4397     case EL_BD_BUTTERFLY_UP:
4398     case EL_BD_BUTTERFLY_LEFT:
4399     case EL_BD_BUTTERFLY_DOWN:
4400       Feld[x][y] = EL_BD_BUTTERFLY;
4401       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4402       break;
4403
4404     case EL_BD_FIREFLY_RIGHT:
4405     case EL_BD_FIREFLY_UP:
4406     case EL_BD_FIREFLY_LEFT:
4407     case EL_BD_FIREFLY_DOWN:
4408       Feld[x][y] = EL_BD_FIREFLY;
4409       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4410       break;
4411
4412     case EL_PACMAN_RIGHT:
4413     case EL_PACMAN_UP:
4414     case EL_PACMAN_LEFT:
4415     case EL_PACMAN_DOWN:
4416       Feld[x][y] = EL_PACMAN;
4417       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4418       break;
4419
4420     case EL_YAMYAM_LEFT:
4421     case EL_YAMYAM_RIGHT:
4422     case EL_YAMYAM_UP:
4423     case EL_YAMYAM_DOWN:
4424       Feld[x][y] = EL_YAMYAM;
4425       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4426       break;
4427
4428     case EL_SP_SNIKSNAK:
4429       MovDir[x][y] = MV_UP;
4430       break;
4431
4432     case EL_SP_ELECTRON:
4433       MovDir[x][y] = MV_LEFT;
4434       break;
4435
4436     case EL_MOLE_LEFT:
4437     case EL_MOLE_RIGHT:
4438     case EL_MOLE_UP:
4439     case EL_MOLE_DOWN:
4440       Feld[x][y] = EL_MOLE;
4441       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4442       break;
4443
4444     default:
4445       if (IS_CUSTOM_ELEMENT(element))
4446       {
4447         struct ElementInfo *ei = &element_info[element];
4448         int move_direction_initial = ei->move_direction_initial;
4449         int move_pattern = ei->move_pattern;
4450
4451         if (move_direction_initial == MV_START_PREVIOUS)
4452         {
4453           if (MovDir[x][y] != MV_NONE)
4454             return;
4455
4456           move_direction_initial = MV_START_AUTOMATIC;
4457         }
4458
4459         if (move_direction_initial == MV_START_RANDOM)
4460           MovDir[x][y] = 1 << RND(4);
4461         else if (move_direction_initial & MV_ANY_DIRECTION)
4462           MovDir[x][y] = move_direction_initial;
4463         else if (move_pattern == MV_ALL_DIRECTIONS ||
4464                  move_pattern == MV_TURNING_LEFT ||
4465                  move_pattern == MV_TURNING_RIGHT ||
4466                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4467                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4468                  move_pattern == MV_TURNING_RANDOM)
4469           MovDir[x][y] = 1 << RND(4);
4470         else if (move_pattern == MV_HORIZONTAL)
4471           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4472         else if (move_pattern == MV_VERTICAL)
4473           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4474         else if (move_pattern & MV_ANY_DIRECTION)
4475           MovDir[x][y] = element_info[element].move_pattern;
4476         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4477                  move_pattern == MV_ALONG_RIGHT_SIDE)
4478         {
4479           /* use random direction as default start direction */
4480           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4481             MovDir[x][y] = 1 << RND(4);
4482
4483           for (i = 0; i < NUM_DIRECTIONS; i++)
4484           {
4485             int x1 = x + xy[i][0];
4486             int y1 = y + xy[i][1];
4487
4488             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4489             {
4490               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4491                 MovDir[x][y] = direction[0][i];
4492               else
4493                 MovDir[x][y] = direction[1][i];
4494
4495               break;
4496             }
4497           }
4498         }                
4499       }
4500       else
4501       {
4502         MovDir[x][y] = 1 << RND(4);
4503
4504         if (element != EL_BUG &&
4505             element != EL_SPACESHIP &&
4506             element != EL_BD_BUTTERFLY &&
4507             element != EL_BD_FIREFLY)
4508           break;
4509
4510         for (i = 0; i < NUM_DIRECTIONS; i++)
4511         {
4512           int x1 = x + xy[i][0];
4513           int y1 = y + xy[i][1];
4514
4515           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4516           {
4517             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4518             {
4519               MovDir[x][y] = direction[0][i];
4520               break;
4521             }
4522             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4523                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4524             {
4525               MovDir[x][y] = direction[1][i];
4526               break;
4527             }
4528           }
4529         }
4530       }
4531       break;
4532   }
4533
4534   GfxDir[x][y] = MovDir[x][y];
4535 }
4536
4537 void InitAmoebaNr(int x, int y)
4538 {
4539   int i;
4540   int group_nr = AmoebeNachbarNr(x, y);
4541
4542   if (group_nr == 0)
4543   {
4544     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4545     {
4546       if (AmoebaCnt[i] == 0)
4547       {
4548         group_nr = i;
4549         break;
4550       }
4551     }
4552   }
4553
4554   AmoebaNr[x][y] = group_nr;
4555   AmoebaCnt[group_nr]++;
4556   AmoebaCnt2[group_nr]++;
4557 }
4558
4559 static void PlayerWins(struct PlayerInfo *player)
4560 {
4561   player->LevelSolved = TRUE;
4562   player->GameOver = TRUE;
4563
4564   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4565                          level.native_em_level->lev->score : player->score);
4566
4567   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4568   player->LevelSolved_CountingScore = player->score_final;
4569 }
4570
4571 void GameWon()
4572 {
4573   static int time, time_final;
4574   static int score, score_final;
4575   static int game_over_delay_1 = 0;
4576   static int game_over_delay_2 = 0;
4577   int game_over_delay_value_1 = 50;
4578   int game_over_delay_value_2 = 50;
4579
4580   if (!local_player->LevelSolved_GameWon)
4581   {
4582     int i;
4583
4584     /* do not start end game actions before the player stops moving (to exit) */
4585     if (local_player->MovPos)
4586       return;
4587
4588     local_player->LevelSolved_GameWon = TRUE;
4589     local_player->LevelSolved_SaveTape = tape.recording;
4590     local_player->LevelSolved_SaveScore = !tape.playing;
4591
4592     if (tape.auto_play)         /* tape might already be stopped here */
4593       tape.auto_play_level_solved = TRUE;
4594
4595 #if 1
4596     TapeStop();
4597 #endif
4598
4599     game_over_delay_1 = game_over_delay_value_1;
4600     game_over_delay_2 = game_over_delay_value_2;
4601
4602     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4603     score = score_final = local_player->score_final;
4604
4605     if (TimeLeft > 0)
4606     {
4607       time_final = 0;
4608       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4609     }
4610     else if (level.time == 0 && TimePlayed < 999)
4611     {
4612       time_final = 999;
4613       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4614     }
4615
4616     local_player->score_final = score_final;
4617
4618     if (level_editor_test_game)
4619     {
4620       time = time_final;
4621       score = score_final;
4622
4623 #if 1
4624       local_player->LevelSolved_CountingTime = time;
4625       local_player->LevelSolved_CountingScore = score;
4626
4627       game_panel_controls[GAME_PANEL_TIME].value = time;
4628       game_panel_controls[GAME_PANEL_SCORE].value = score;
4629
4630       DisplayGameControlValues();
4631 #else
4632       DrawGameValue_Time(time);
4633       DrawGameValue_Score(score);
4634 #endif
4635     }
4636
4637     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4638     {
4639       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4640       {
4641         /* close exit door after last player */
4642         if ((AllPlayersGone &&
4643              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4644               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4645               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4646             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4647             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4648         {
4649           int element = Feld[ExitX][ExitY];
4650
4651 #if 0
4652           if (element == EL_EM_EXIT_OPEN ||
4653               element == EL_EM_STEEL_EXIT_OPEN)
4654           {
4655             Bang(ExitX, ExitY);
4656           }
4657           else
4658 #endif
4659           {
4660             Feld[ExitX][ExitY] =
4661               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4662                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4663                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4664                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4665                EL_EM_STEEL_EXIT_CLOSING);
4666
4667             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4668           }
4669         }
4670
4671         /* player disappears */
4672         DrawLevelField(ExitX, ExitY);
4673       }
4674
4675       for (i = 0; i < MAX_PLAYERS; i++)
4676       {
4677         struct PlayerInfo *player = &stored_player[i];
4678
4679         if (player->present)
4680         {
4681           RemovePlayer(player);
4682
4683           /* player disappears */
4684           DrawLevelField(player->jx, player->jy);
4685         }
4686       }
4687     }
4688
4689     PlaySound(SND_GAME_WINNING);
4690   }
4691
4692   if (game_over_delay_1 > 0)
4693   {
4694     game_over_delay_1--;
4695
4696     return;
4697   }
4698
4699   if (time != time_final)
4700   {
4701     int time_to_go = ABS(time_final - time);
4702     int time_count_dir = (time < time_final ? +1 : -1);
4703     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4704
4705     time  += time_count_steps * time_count_dir;
4706     score += time_count_steps * level.score[SC_TIME_BONUS];
4707
4708 #if 1
4709     local_player->LevelSolved_CountingTime = time;
4710     local_player->LevelSolved_CountingScore = score;
4711
4712     game_panel_controls[GAME_PANEL_TIME].value = time;
4713     game_panel_controls[GAME_PANEL_SCORE].value = score;
4714
4715     DisplayGameControlValues();
4716 #else
4717     DrawGameValue_Time(time);
4718     DrawGameValue_Score(score);
4719 #endif
4720
4721     if (time == time_final)
4722       StopSound(SND_GAME_LEVELTIME_BONUS);
4723     else if (setup.sound_loops)
4724       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4725     else
4726       PlaySound(SND_GAME_LEVELTIME_BONUS);
4727
4728     return;
4729   }
4730
4731   local_player->LevelSolved_PanelOff = TRUE;
4732
4733   if (game_over_delay_2 > 0)
4734   {
4735     game_over_delay_2--;
4736
4737     return;
4738   }
4739
4740 #if 1
4741   GameEnd();
4742 #endif
4743 }
4744
4745 void GameEnd()
4746 {
4747   int hi_pos;
4748   boolean raise_level = FALSE;
4749
4750   local_player->LevelSolved_GameEnd = TRUE;
4751
4752   CloseDoor(DOOR_CLOSE_1);
4753
4754   if (local_player->LevelSolved_SaveTape)
4755   {
4756 #if 0
4757     TapeStop();
4758 #endif
4759
4760 #if 1
4761     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4762 #else
4763     SaveTape(tape.level_nr);            /* ask to save tape */
4764 #endif
4765   }
4766
4767   if (level_editor_test_game)
4768   {
4769     game_status = GAME_MODE_MAIN;
4770
4771 #if 1
4772     DrawAndFadeInMainMenu(REDRAW_FIELD);
4773 #else
4774     DrawMainMenu();
4775 #endif
4776
4777     return;
4778   }
4779
4780   if (!local_player->LevelSolved_SaveScore)
4781   {
4782 #if 1
4783     FadeOut(REDRAW_FIELD);
4784 #endif
4785
4786     game_status = GAME_MODE_MAIN;
4787
4788     DrawAndFadeInMainMenu(REDRAW_FIELD);
4789
4790     return;
4791   }
4792
4793   if (level_nr == leveldir_current->handicap_level)
4794   {
4795     leveldir_current->handicap_level++;
4796     SaveLevelSetup_SeriesInfo();
4797   }
4798
4799   if (level_nr < leveldir_current->last_level)
4800     raise_level = TRUE;                 /* advance to next level */
4801
4802   if ((hi_pos = NewHiScore()) >= 0) 
4803   {
4804     game_status = GAME_MODE_SCORES;
4805
4806     DrawHallOfFame(hi_pos);
4807
4808     if (raise_level)
4809     {
4810       level_nr++;
4811       TapeErase();
4812     }
4813   }
4814   else
4815   {
4816 #if 1
4817     FadeOut(REDRAW_FIELD);
4818 #endif
4819
4820     game_status = GAME_MODE_MAIN;
4821
4822     if (raise_level)
4823     {
4824       level_nr++;
4825       TapeErase();
4826     }
4827
4828     DrawAndFadeInMainMenu(REDRAW_FIELD);
4829   }
4830 }
4831
4832 int NewHiScore()
4833 {
4834   int k, l;
4835   int position = -1;
4836
4837   LoadScore(level_nr);
4838
4839   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4840       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4841     return -1;
4842
4843   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4844   {
4845     if (local_player->score_final > highscore[k].Score)
4846     {
4847       /* player has made it to the hall of fame */
4848
4849       if (k < MAX_SCORE_ENTRIES - 1)
4850       {
4851         int m = MAX_SCORE_ENTRIES - 1;
4852
4853 #ifdef ONE_PER_NAME
4854         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4855           if (strEqual(setup.player_name, highscore[l].Name))
4856             m = l;
4857         if (m == k)     /* player's new highscore overwrites his old one */
4858           goto put_into_list;
4859 #endif
4860
4861         for (l = m; l > k; l--)
4862         {
4863           strcpy(highscore[l].Name, highscore[l - 1].Name);
4864           highscore[l].Score = highscore[l - 1].Score;
4865         }
4866       }
4867
4868 #ifdef ONE_PER_NAME
4869       put_into_list:
4870 #endif
4871       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4872       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4873       highscore[k].Score = local_player->score_final; 
4874       position = k;
4875       break;
4876     }
4877
4878 #ifdef ONE_PER_NAME
4879     else if (!strncmp(setup.player_name, highscore[k].Name,
4880                       MAX_PLAYER_NAME_LEN))
4881       break;    /* player already there with a higher score */
4882 #endif
4883
4884   }
4885
4886   if (position >= 0) 
4887     SaveScore(level_nr);
4888
4889   return position;
4890 }
4891
4892 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4893 {
4894   int element = Feld[x][y];
4895   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4896   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4897   int horiz_move = (dx != 0);
4898   int sign = (horiz_move ? dx : dy);
4899   int step = sign * element_info[element].move_stepsize;
4900
4901   /* special values for move stepsize for spring and things on conveyor belt */
4902   if (horiz_move)
4903   {
4904     if (CAN_FALL(element) &&
4905         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4906       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4907     else if (element == EL_SPRING)
4908       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4909   }
4910
4911   return step;
4912 }
4913
4914 inline static int getElementMoveStepsize(int x, int y)
4915 {
4916   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4917 }
4918
4919 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4920 {
4921   if (player->GfxAction != action || player->GfxDir != dir)
4922   {
4923 #if 0
4924     printf("Player frame reset! (%d => %d, %d => %d)\n",
4925            player->GfxAction, action, player->GfxDir, dir);
4926 #endif
4927
4928     player->GfxAction = action;
4929     player->GfxDir = dir;
4930     player->Frame = 0;
4931     player->StepFrame = 0;
4932   }
4933 }
4934
4935 #if USE_GFX_RESET_GFX_ANIMATION
4936 static void ResetGfxFrame(int x, int y, boolean redraw)
4937 {
4938   int element = Feld[x][y];
4939   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4940   int last_gfx_frame = GfxFrame[x][y];
4941
4942   if (graphic_info[graphic].anim_global_sync)
4943     GfxFrame[x][y] = FrameCounter;
4944   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4945     GfxFrame[x][y] = CustomValue[x][y];
4946   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4947     GfxFrame[x][y] = element_info[element].collect_score;
4948   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4949     GfxFrame[x][y] = ChangeDelay[x][y];
4950
4951   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4952     DrawLevelGraphicAnimation(x, y, graphic);
4953 }
4954 #endif
4955
4956 static void ResetGfxAnimation(int x, int y)
4957 {
4958   GfxAction[x][y] = ACTION_DEFAULT;
4959   GfxDir[x][y] = MovDir[x][y];
4960   GfxFrame[x][y] = 0;
4961
4962 #if USE_GFX_RESET_GFX_ANIMATION
4963   ResetGfxFrame(x, y, FALSE);
4964 #endif
4965 }
4966
4967 static void ResetRandomAnimationValue(int x, int y)
4968 {
4969   GfxRandom[x][y] = INIT_GFX_RANDOM();
4970 }
4971
4972 void InitMovingField(int x, int y, int direction)
4973 {
4974   int element = Feld[x][y];
4975   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4976   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4977   int newx = x + dx;
4978   int newy = y + dy;
4979   boolean is_moving_before, is_moving_after;
4980 #if 0
4981   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4982 #endif
4983
4984   /* check if element was/is moving or being moved before/after mode change */
4985 #if 1
4986 #if 1
4987   is_moving_before = (WasJustMoving[x][y] != 0);
4988 #else
4989   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4990   is_moving_before = WasJustMoving[x][y];
4991 #endif
4992 #else
4993   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4994 #endif
4995   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4996
4997   /* reset animation only for moving elements which change direction of moving
4998      or which just started or stopped moving
4999      (else CEs with property "can move" / "not moving" are reset each frame) */
5000 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5001 #if 1
5002   if (is_moving_before != is_moving_after ||
5003       direction != MovDir[x][y])
5004     ResetGfxAnimation(x, y);
5005 #else
5006   if ((is_moving_before || is_moving_after) && !continues_moving)
5007     ResetGfxAnimation(x, y);
5008 #endif
5009 #else
5010   if (!continues_moving)
5011     ResetGfxAnimation(x, y);
5012 #endif
5013
5014   MovDir[x][y] = direction;
5015   GfxDir[x][y] = direction;
5016
5017 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5018   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5019                      direction == MV_DOWN && CAN_FALL(element) ?
5020                      ACTION_FALLING : ACTION_MOVING);
5021 #else
5022   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5023                      ACTION_FALLING : ACTION_MOVING);
5024 #endif
5025
5026   /* this is needed for CEs with property "can move" / "not moving" */
5027
5028   if (is_moving_after)
5029   {
5030     if (Feld[newx][newy] == EL_EMPTY)
5031       Feld[newx][newy] = EL_BLOCKED;
5032
5033     MovDir[newx][newy] = MovDir[x][y];
5034
5035 #if USE_NEW_CUSTOM_VALUE
5036     CustomValue[newx][newy] = CustomValue[x][y];
5037 #endif
5038
5039     GfxFrame[newx][newy] = GfxFrame[x][y];
5040     GfxRandom[newx][newy] = GfxRandom[x][y];
5041     GfxAction[newx][newy] = GfxAction[x][y];
5042     GfxDir[newx][newy] = GfxDir[x][y];
5043   }
5044 }
5045
5046 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5047 {
5048   int direction = MovDir[x][y];
5049   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5050   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5051
5052   *goes_to_x = newx;
5053   *goes_to_y = newy;
5054 }
5055
5056 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5057 {
5058   int oldx = x, oldy = y;
5059   int direction = MovDir[x][y];
5060
5061   if (direction == MV_LEFT)
5062     oldx++;
5063   else if (direction == MV_RIGHT)
5064     oldx--;
5065   else if (direction == MV_UP)
5066     oldy++;
5067   else if (direction == MV_DOWN)
5068     oldy--;
5069
5070   *comes_from_x = oldx;
5071   *comes_from_y = oldy;
5072 }
5073
5074 int MovingOrBlocked2Element(int x, int y)
5075 {
5076   int element = Feld[x][y];
5077
5078   if (element == EL_BLOCKED)
5079   {
5080     int oldx, oldy;
5081
5082     Blocked2Moving(x, y, &oldx, &oldy);
5083     return Feld[oldx][oldy];
5084   }
5085   else
5086     return element;
5087 }
5088
5089 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5090 {
5091   /* like MovingOrBlocked2Element(), but if element is moving
5092      and (x,y) is the field the moving element is just leaving,
5093      return EL_BLOCKED instead of the element value */
5094   int element = Feld[x][y];
5095
5096   if (IS_MOVING(x, y))
5097   {
5098     if (element == EL_BLOCKED)
5099     {
5100       int oldx, oldy;
5101
5102       Blocked2Moving(x, y, &oldx, &oldy);
5103       return Feld[oldx][oldy];
5104     }
5105     else
5106       return EL_BLOCKED;
5107   }
5108   else
5109     return element;
5110 }
5111
5112 static void RemoveField(int x, int y)
5113 {
5114   Feld[x][y] = EL_EMPTY;
5115
5116   MovPos[x][y] = 0;
5117   MovDir[x][y] = 0;
5118   MovDelay[x][y] = 0;
5119
5120 #if USE_NEW_CUSTOM_VALUE
5121   CustomValue[x][y] = 0;
5122 #endif
5123
5124   AmoebaNr[x][y] = 0;
5125   ChangeDelay[x][y] = 0;
5126   ChangePage[x][y] = -1;
5127   Pushed[x][y] = FALSE;
5128
5129 #if 0
5130   ExplodeField[x][y] = EX_TYPE_NONE;
5131 #endif
5132
5133   GfxElement[x][y] = EL_UNDEFINED;
5134   GfxAction[x][y] = ACTION_DEFAULT;
5135   GfxDir[x][y] = MV_NONE;
5136   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5137 }
5138
5139 void RemoveMovingField(int x, int y)
5140 {
5141   int oldx = x, oldy = y, newx = x, newy = y;
5142   int element = Feld[x][y];
5143   int next_element = EL_UNDEFINED;
5144
5145   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5146     return;
5147
5148   if (IS_MOVING(x, y))
5149   {
5150     Moving2Blocked(x, y, &newx, &newy);
5151
5152     if (Feld[newx][newy] != EL_BLOCKED)
5153     {
5154       /* element is moving, but target field is not free (blocked), but
5155          already occupied by something different (example: acid pool);
5156          in this case, only remove the moving field, but not the target */
5157
5158       RemoveField(oldx, oldy);
5159
5160       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5161
5162       TEST_DrawLevelField(oldx, oldy);
5163
5164       return;
5165     }
5166   }
5167   else if (element == EL_BLOCKED)
5168   {
5169     Blocked2Moving(x, y, &oldx, &oldy);
5170     if (!IS_MOVING(oldx, oldy))
5171       return;
5172   }
5173
5174   if (element == EL_BLOCKED &&
5175       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5176        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5177        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5178        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5179        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5180        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5181     next_element = get_next_element(Feld[oldx][oldy]);
5182
5183   RemoveField(oldx, oldy);
5184   RemoveField(newx, newy);
5185
5186   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5187
5188   if (next_element != EL_UNDEFINED)
5189     Feld[oldx][oldy] = next_element;
5190
5191   TEST_DrawLevelField(oldx, oldy);
5192   TEST_DrawLevelField(newx, newy);
5193 }
5194
5195 void DrawDynamite(int x, int y)
5196 {
5197   int sx = SCREENX(x), sy = SCREENY(y);
5198   int graphic = el2img(Feld[x][y]);
5199   int frame;
5200
5201   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5202     return;
5203
5204   if (IS_WALKABLE_INSIDE(Back[x][y]))
5205     return;
5206
5207   if (Back[x][y])
5208     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5209   else if (Store[x][y])
5210     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5211
5212   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5213
5214   if (Back[x][y] || Store[x][y])
5215     DrawGraphicThruMask(sx, sy, graphic, frame);
5216   else
5217     DrawGraphic(sx, sy, graphic, frame);
5218 }
5219
5220 void CheckDynamite(int x, int y)
5221 {
5222   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5223   {
5224     MovDelay[x][y]--;
5225
5226     if (MovDelay[x][y] != 0)
5227     {
5228       DrawDynamite(x, y);
5229       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5230
5231       return;
5232     }
5233   }
5234
5235   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5236
5237   Bang(x, y);
5238 }
5239
5240 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5241 {
5242   boolean num_checked_players = 0;
5243   int i;
5244
5245   for (i = 0; i < MAX_PLAYERS; i++)
5246   {
5247     if (stored_player[i].active)
5248     {
5249       int sx = stored_player[i].jx;
5250       int sy = stored_player[i].jy;
5251
5252       if (num_checked_players == 0)
5253       {
5254         *sx1 = *sx2 = sx;
5255         *sy1 = *sy2 = sy;
5256       }
5257       else
5258       {
5259         *sx1 = MIN(*sx1, sx);
5260         *sy1 = MIN(*sy1, sy);
5261         *sx2 = MAX(*sx2, sx);
5262         *sy2 = MAX(*sy2, sy);
5263       }
5264
5265       num_checked_players++;
5266     }
5267   }
5268 }
5269
5270 static boolean checkIfAllPlayersFitToScreen_RND()
5271 {
5272   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5273
5274   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5275
5276   return (sx2 - sx1 < SCR_FIELDX &&
5277           sy2 - sy1 < SCR_FIELDY);
5278 }
5279
5280 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5281 {
5282   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5283
5284   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5285
5286   *sx = (sx1 + sx2) / 2;
5287   *sy = (sy1 + sy2) / 2;
5288 }
5289
5290 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5291                         boolean center_screen, boolean quick_relocation)
5292 {
5293   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5294   boolean no_delay = (tape.warp_forward);
5295   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5296   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5297
5298   if (quick_relocation)
5299   {
5300     int offset = game.scroll_delay_value;
5301
5302     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5303     {
5304       if (!level.shifted_relocation || center_screen)
5305       {
5306         /* quick relocation (without scrolling), with centering of screen */
5307
5308         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5309                     x > SBX_Right + MIDPOSX ? SBX_Right :
5310                     x - MIDPOSX);
5311
5312         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5313                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5314                     y - MIDPOSY);
5315       }
5316       else
5317       {
5318         /* quick relocation (without scrolling), but do not center screen */
5319
5320         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5321                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5322                                old_x - MIDPOSX);
5323
5324         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5325                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5326                                old_y - MIDPOSY);
5327
5328         int offset_x = x + (scroll_x - center_scroll_x);
5329         int offset_y = y + (scroll_y - center_scroll_y);
5330
5331         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5332                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5333                     offset_x - MIDPOSX);
5334
5335         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5336                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5337                     offset_y - MIDPOSY);
5338       }
5339     }
5340     else
5341     {
5342       /* quick relocation (without scrolling), inside visible screen area */
5343
5344       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5345           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5346         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5347
5348       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5349           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5350         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5351
5352       /* don't scroll over playfield boundaries */
5353       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5354         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5355
5356       /* don't scroll over playfield boundaries */
5357       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5358         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5359     }
5360
5361     RedrawPlayfield(TRUE, 0,0,0,0);
5362   }
5363   else
5364   {
5365 #if 1
5366     int scroll_xx, scroll_yy;
5367
5368     if (!level.shifted_relocation || center_screen)
5369     {
5370       /* visible relocation (with scrolling), with centering of screen */
5371
5372       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5373                    x > SBX_Right + MIDPOSX ? SBX_Right :
5374                    x - MIDPOSX);
5375
5376       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5377                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5378                    y - MIDPOSY);
5379     }
5380     else
5381     {
5382       /* visible relocation (with scrolling), but do not center screen */
5383
5384       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5385                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5386                              old_x - MIDPOSX);
5387
5388       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5389                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5390                              old_y - MIDPOSY);
5391
5392       int offset_x = x + (scroll_x - center_scroll_x);
5393       int offset_y = y + (scroll_y - center_scroll_y);
5394
5395       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5396                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5397                    offset_x - MIDPOSX);
5398
5399       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5400                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5401                    offset_y - MIDPOSY);
5402     }
5403
5404 #else
5405
5406     /* visible relocation (with scrolling), with centering of screen */
5407
5408     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5409                      x > SBX_Right + MIDPOSX ? SBX_Right :
5410                      x - MIDPOSX);
5411
5412     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5413                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5414                      y - MIDPOSY);
5415 #endif
5416
5417     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5418
5419     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5420     {
5421       int dx = 0, dy = 0;
5422       int fx = FX, fy = FY;
5423
5424       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5425       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5426
5427       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5428         break;
5429
5430       scroll_x -= dx;
5431       scroll_y -= dy;
5432
5433       fx += dx * TILEX / 2;
5434       fy += dy * TILEY / 2;
5435
5436       ScrollLevel(dx, dy);
5437       DrawAllPlayers();
5438
5439       /* scroll in two steps of half tile size to make things smoother */
5440       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5441       FlushDisplay();
5442       Delay(wait_delay_value);
5443
5444       /* scroll second step to align at full tile size */
5445       BackToFront();
5446       Delay(wait_delay_value);
5447     }
5448
5449     DrawAllPlayers();
5450     BackToFront();
5451     Delay(wait_delay_value);
5452   }
5453 }
5454
5455 void RelocatePlayer(int jx, int jy, int el_player_raw)
5456 {
5457   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5458   int player_nr = GET_PLAYER_NR(el_player);
5459   struct PlayerInfo *player = &stored_player[player_nr];
5460   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5461   boolean no_delay = (tape.warp_forward);
5462   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5463   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5464   int old_jx = player->jx;
5465   int old_jy = player->jy;
5466   int old_element = Feld[old_jx][old_jy];
5467   int element = Feld[jx][jy];
5468   boolean player_relocated = (old_jx != jx || old_jy != jy);
5469
5470   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5471   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5472   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5473   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5474   int leave_side_horiz = move_dir_horiz;
5475   int leave_side_vert  = move_dir_vert;
5476   int enter_side = enter_side_horiz | enter_side_vert;
5477   int leave_side = leave_side_horiz | leave_side_vert;
5478
5479   if (player->GameOver)         /* do not reanimate dead player */
5480     return;
5481
5482   if (!player_relocated)        /* no need to relocate the player */
5483     return;
5484
5485   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5486   {
5487     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5488     DrawLevelField(jx, jy);
5489   }
5490
5491   if (player->present)
5492   {
5493     while (player->MovPos)
5494     {
5495       ScrollPlayer(player, SCROLL_GO_ON);
5496       ScrollScreen(NULL, SCROLL_GO_ON);
5497
5498       AdvanceFrameAndPlayerCounters(player->index_nr);
5499
5500       DrawPlayer(player);
5501
5502       BackToFront();
5503       Delay(wait_delay_value);
5504     }
5505
5506     DrawPlayer(player);         /* needed here only to cleanup last field */
5507     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5508
5509     player->is_moving = FALSE;
5510   }
5511
5512   if (IS_CUSTOM_ELEMENT(old_element))
5513     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5514                                CE_LEFT_BY_PLAYER,
5515                                player->index_bit, leave_side);
5516
5517   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5518                                       CE_PLAYER_LEAVES_X,
5519                                       player->index_bit, leave_side);
5520
5521   Feld[jx][jy] = el_player;
5522   InitPlayerField(jx, jy, el_player, TRUE);
5523
5524   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5525   {
5526     Feld[jx][jy] = element;
5527     InitField(jx, jy, FALSE);
5528   }
5529
5530   /* only visually relocate centered player */
5531   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5532                      FALSE, level.instant_relocation);
5533
5534   TestIfPlayerTouchesBadThing(jx, jy);
5535   TestIfPlayerTouchesCustomElement(jx, jy);
5536
5537   if (IS_CUSTOM_ELEMENT(element))
5538     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5539                                player->index_bit, enter_side);
5540
5541   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5542                                       player->index_bit, enter_side);
5543 }
5544
5545 void Explode(int ex, int ey, int phase, int mode)
5546 {
5547   int x, y;
5548   int last_phase;
5549   int border_element;
5550
5551   /* !!! eliminate this variable !!! */
5552   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5553
5554   if (game.explosions_delayed)
5555   {
5556     ExplodeField[ex][ey] = mode;
5557     return;
5558   }
5559
5560   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5561   {
5562     int center_element = Feld[ex][ey];
5563     int artwork_element, explosion_element;     /* set these values later */
5564
5565 #if 0
5566     /* --- This is only really needed (and now handled) in "Impact()". --- */
5567     /* do not explode moving elements that left the explode field in time */
5568     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5569         center_element == EL_EMPTY &&
5570         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5571       return;
5572 #endif
5573
5574 #if 0
5575     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5576     if (mode == EX_TYPE_NORMAL ||
5577         mode == EX_TYPE_CENTER ||
5578         mode == EX_TYPE_CROSS)
5579       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5580 #endif
5581
5582     /* remove things displayed in background while burning dynamite */
5583     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5584       Back[ex][ey] = 0;
5585
5586     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5587     {
5588       /* put moving element to center field (and let it explode there) */
5589       center_element = MovingOrBlocked2Element(ex, ey);
5590       RemoveMovingField(ex, ey);
5591       Feld[ex][ey] = center_element;
5592     }
5593
5594     /* now "center_element" is finally determined -- set related values now */
5595     artwork_element = center_element;           /* for custom player artwork */
5596     explosion_element = center_element;         /* for custom player artwork */
5597
5598     if (IS_PLAYER(ex, ey))
5599     {
5600       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5601
5602       artwork_element = stored_player[player_nr].artwork_element;
5603
5604       if (level.use_explosion_element[player_nr])
5605       {
5606         explosion_element = level.explosion_element[player_nr];
5607         artwork_element = explosion_element;
5608       }
5609     }
5610
5611 #if 1
5612     if (mode == EX_TYPE_NORMAL ||
5613         mode == EX_TYPE_CENTER ||
5614         mode == EX_TYPE_CROSS)
5615       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5616 #endif
5617
5618     last_phase = element_info[explosion_element].explosion_delay + 1;
5619
5620     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5621     {
5622       int xx = x - ex + 1;
5623       int yy = y - ey + 1;
5624       int element;
5625
5626       if (!IN_LEV_FIELD(x, y) ||
5627           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5628           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5629         continue;
5630
5631       element = Feld[x][y];
5632
5633       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5634       {
5635         element = MovingOrBlocked2Element(x, y);
5636
5637         if (!IS_EXPLOSION_PROOF(element))
5638           RemoveMovingField(x, y);
5639       }
5640
5641       /* indestructible elements can only explode in center (but not flames) */
5642       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5643                                            mode == EX_TYPE_BORDER)) ||
5644           element == EL_FLAMES)
5645         continue;
5646
5647       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5648          behaviour, for example when touching a yamyam that explodes to rocks
5649          with active deadly shield, a rock is created under the player !!! */
5650       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5651 #if 0
5652       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5653           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5654            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5655 #else
5656       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5657 #endif
5658       {
5659         if (IS_ACTIVE_BOMB(element))
5660         {
5661           /* re-activate things under the bomb like gate or penguin */
5662           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5663           Back[x][y] = 0;
5664         }
5665
5666         continue;
5667       }
5668
5669       /* save walkable background elements while explosion on same tile */
5670       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5671           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5672         Back[x][y] = element;
5673
5674       /* ignite explodable elements reached by other explosion */
5675       if (element == EL_EXPLOSION)
5676         element = Store2[x][y];
5677
5678       if (AmoebaNr[x][y] &&
5679           (element == EL_AMOEBA_FULL ||
5680            element == EL_BD_AMOEBA ||
5681            element == EL_AMOEBA_GROWING))
5682       {
5683         AmoebaCnt[AmoebaNr[x][y]]--;
5684         AmoebaCnt2[AmoebaNr[x][y]]--;
5685       }
5686
5687       RemoveField(x, y);
5688
5689       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5690       {
5691         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5692
5693         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5694
5695         if (PLAYERINFO(ex, ey)->use_murphy)
5696           Store[x][y] = EL_EMPTY;
5697       }
5698
5699       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5700          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5701       else if (ELEM_IS_PLAYER(center_element))
5702         Store[x][y] = EL_EMPTY;
5703       else if (center_element == EL_YAMYAM)
5704         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5705       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5706         Store[x][y] = element_info[center_element].content.e[xx][yy];
5707 #if 1
5708       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5709          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5710          otherwise) -- FIX THIS !!! */
5711       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5712         Store[x][y] = element_info[element].content.e[1][1];
5713 #else
5714       else if (!CAN_EXPLODE(element))
5715         Store[x][y] = element_info[element].content.e[1][1];
5716 #endif
5717       else
5718         Store[x][y] = EL_EMPTY;
5719
5720       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5721           center_element == EL_AMOEBA_TO_DIAMOND)
5722         Store2[x][y] = element;
5723
5724       Feld[x][y] = EL_EXPLOSION;
5725       GfxElement[x][y] = artwork_element;
5726
5727       ExplodePhase[x][y] = 1;
5728       ExplodeDelay[x][y] = last_phase;
5729
5730       Stop[x][y] = TRUE;
5731     }
5732
5733     if (center_element == EL_YAMYAM)
5734       game.yamyam_content_nr =
5735         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5736
5737     return;
5738   }
5739
5740   if (Stop[ex][ey])
5741     return;
5742
5743   x = ex;
5744   y = ey;
5745
5746   if (phase == 1)
5747     GfxFrame[x][y] = 0;         /* restart explosion animation */
5748
5749   last_phase = ExplodeDelay[x][y];
5750
5751   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5752
5753 #ifdef DEBUG
5754
5755   /* activate this even in non-DEBUG version until cause for crash in
5756      getGraphicAnimationFrame() (see below) is found and eliminated */
5757
5758 #endif
5759 #if 1
5760
5761 #if 1
5762   /* this can happen if the player leaves an explosion just in time */
5763   if (GfxElement[x][y] == EL_UNDEFINED)
5764     GfxElement[x][y] = EL_EMPTY;
5765 #else
5766   if (GfxElement[x][y] == EL_UNDEFINED)
5767   {
5768     printf("\n\n");
5769     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5770     printf("Explode(): This should never happen!\n");
5771     printf("\n\n");
5772
5773     GfxElement[x][y] = EL_EMPTY;
5774   }
5775 #endif
5776
5777 #endif
5778
5779   border_element = Store2[x][y];
5780   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5781     border_element = StorePlayer[x][y];
5782
5783   if (phase == element_info[border_element].ignition_delay ||
5784       phase == last_phase)
5785   {
5786     boolean border_explosion = FALSE;
5787
5788     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5789         !PLAYER_EXPLOSION_PROTECTED(x, y))
5790     {
5791       KillPlayerUnlessExplosionProtected(x, y);
5792       border_explosion = TRUE;
5793     }
5794     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5795     {
5796       Feld[x][y] = Store2[x][y];
5797       Store2[x][y] = 0;
5798       Bang(x, y);
5799       border_explosion = TRUE;
5800     }
5801     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5802     {
5803       AmoebeUmwandeln(x, y);
5804       Store2[x][y] = 0;
5805       border_explosion = TRUE;
5806     }
5807
5808     /* if an element just explodes due to another explosion (chain-reaction),
5809        do not immediately end the new explosion when it was the last frame of
5810        the explosion (as it would be done in the following "if"-statement!) */
5811     if (border_explosion && phase == last_phase)
5812       return;
5813   }
5814
5815   if (phase == last_phase)
5816   {
5817     int element;
5818
5819     element = Feld[x][y] = Store[x][y];
5820     Store[x][y] = Store2[x][y] = 0;
5821     GfxElement[x][y] = EL_UNDEFINED;
5822
5823     /* player can escape from explosions and might therefore be still alive */
5824     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5825         element <= EL_PLAYER_IS_EXPLODING_4)
5826     {
5827       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5828       int explosion_element = EL_PLAYER_1 + player_nr;
5829       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5830       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5831
5832       if (level.use_explosion_element[player_nr])
5833         explosion_element = level.explosion_element[player_nr];
5834
5835       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5836                     element_info[explosion_element].content.e[xx][yy]);
5837     }
5838
5839     /* restore probably existing indestructible background element */
5840     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5841       element = Feld[x][y] = Back[x][y];
5842     Back[x][y] = 0;
5843
5844     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5845     GfxDir[x][y] = MV_NONE;
5846     ChangeDelay[x][y] = 0;
5847     ChangePage[x][y] = -1;
5848
5849 #if USE_NEW_CUSTOM_VALUE
5850     CustomValue[x][y] = 0;
5851 #endif
5852
5853     InitField_WithBug2(x, y, FALSE);
5854
5855     TEST_DrawLevelField(x, y);
5856
5857     TestIfElementTouchesCustomElement(x, y);
5858
5859     if (GFX_CRUMBLED(element))
5860       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5861
5862     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5863       StorePlayer[x][y] = 0;
5864
5865     if (ELEM_IS_PLAYER(element))
5866       RelocatePlayer(x, y, element);
5867   }
5868   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5869   {
5870     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5871     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5872
5873     if (phase == delay)
5874       TEST_DrawLevelFieldCrumbledSand(x, y);
5875
5876     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5877     {
5878       DrawLevelElement(x, y, Back[x][y]);
5879       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5880     }
5881     else if (IS_WALKABLE_UNDER(Back[x][y]))
5882     {
5883       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5884       DrawLevelElementThruMask(x, y, Back[x][y]);
5885     }
5886     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5887       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5888   }
5889 }
5890
5891 void DynaExplode(int ex, int ey)
5892 {
5893   int i, j;
5894   int dynabomb_element = Feld[ex][ey];
5895   int dynabomb_size = 1;
5896   boolean dynabomb_xl = FALSE;
5897   struct PlayerInfo *player;
5898   static int xy[4][2] =
5899   {
5900     { 0, -1 },
5901     { -1, 0 },
5902     { +1, 0 },
5903     { 0, +1 }
5904   };
5905
5906   if (IS_ACTIVE_BOMB(dynabomb_element))
5907   {
5908     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5909     dynabomb_size = player->dynabomb_size;
5910     dynabomb_xl = player->dynabomb_xl;
5911     player->dynabombs_left++;
5912   }
5913
5914   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5915
5916   for (i = 0; i < NUM_DIRECTIONS; i++)
5917   {
5918     for (j = 1; j <= dynabomb_size; j++)
5919     {
5920       int x = ex + j * xy[i][0];
5921       int y = ey + j * xy[i][1];
5922       int element;
5923
5924       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5925         break;
5926
5927       element = Feld[x][y];
5928
5929       /* do not restart explosions of fields with active bombs */
5930       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5931         continue;
5932
5933       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5934
5935       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5936           !IS_DIGGABLE(element) && !dynabomb_xl)
5937         break;
5938     }
5939   }
5940 }
5941
5942 void Bang(int x, int y)
5943 {
5944   int element = MovingOrBlocked2Element(x, y);
5945   int explosion_type = EX_TYPE_NORMAL;
5946
5947   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5948   {
5949     struct PlayerInfo *player = PLAYERINFO(x, y);
5950
5951     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5952                             player->element_nr);
5953
5954     if (level.use_explosion_element[player->index_nr])
5955     {
5956       int explosion_element = level.explosion_element[player->index_nr];
5957
5958       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5959         explosion_type = EX_TYPE_CROSS;
5960       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5961         explosion_type = EX_TYPE_CENTER;
5962     }
5963   }
5964
5965   switch (element)
5966   {
5967     case EL_BUG:
5968     case EL_SPACESHIP:
5969     case EL_BD_BUTTERFLY:
5970     case EL_BD_FIREFLY:
5971     case EL_YAMYAM:
5972     case EL_DARK_YAMYAM:
5973     case EL_ROBOT:
5974     case EL_PACMAN:
5975     case EL_MOLE:
5976       RaiseScoreElement(element);
5977       break;
5978
5979     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5980     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5981     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5982     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5983     case EL_DYNABOMB_INCREASE_NUMBER:
5984     case EL_DYNABOMB_INCREASE_SIZE:
5985     case EL_DYNABOMB_INCREASE_POWER:
5986       explosion_type = EX_TYPE_DYNA;
5987       break;
5988
5989     case EL_DC_LANDMINE:
5990 #if 0
5991     case EL_EM_EXIT_OPEN:
5992     case EL_EM_STEEL_EXIT_OPEN:
5993 #endif
5994       explosion_type = EX_TYPE_CENTER;
5995       break;
5996
5997     case EL_PENGUIN:
5998     case EL_LAMP:
5999     case EL_LAMP_ACTIVE:
6000     case EL_AMOEBA_TO_DIAMOND:
6001       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6002         explosion_type = EX_TYPE_CENTER;
6003       break;
6004
6005     default:
6006       if (element_info[element].explosion_type == EXPLODES_CROSS)
6007         explosion_type = EX_TYPE_CROSS;
6008       else if (element_info[element].explosion_type == EXPLODES_1X1)
6009         explosion_type = EX_TYPE_CENTER;
6010       break;
6011   }
6012
6013   if (explosion_type == EX_TYPE_DYNA)
6014     DynaExplode(x, y);
6015   else
6016     Explode(x, y, EX_PHASE_START, explosion_type);
6017
6018   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6019 }
6020
6021 void SplashAcid(int x, int y)
6022 {
6023   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6024       (!IN_LEV_FIELD(x - 1, y - 2) ||
6025        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6026     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6027
6028   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6029       (!IN_LEV_FIELD(x + 1, y - 2) ||
6030        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6031     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6032
6033   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6034 }
6035
6036 static void InitBeltMovement()
6037 {
6038   static int belt_base_element[4] =
6039   {
6040     EL_CONVEYOR_BELT_1_LEFT,
6041     EL_CONVEYOR_BELT_2_LEFT,
6042     EL_CONVEYOR_BELT_3_LEFT,
6043     EL_CONVEYOR_BELT_4_LEFT
6044   };
6045   static int belt_base_active_element[4] =
6046   {
6047     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6048     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6049     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6050     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6051   };
6052
6053   int x, y, i, j;
6054
6055   /* set frame order for belt animation graphic according to belt direction */
6056   for (i = 0; i < NUM_BELTS; i++)
6057   {
6058     int belt_nr = i;
6059
6060     for (j = 0; j < NUM_BELT_PARTS; j++)
6061     {
6062       int element = belt_base_active_element[belt_nr] + j;
6063       int graphic_1 = el2img(element);
6064       int graphic_2 = el2panelimg(element);
6065
6066       if (game.belt_dir[i] == MV_LEFT)
6067       {
6068         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6069         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6070       }
6071       else
6072       {
6073         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6074         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6075       }
6076     }
6077   }
6078
6079   SCAN_PLAYFIELD(x, y)
6080   {
6081     int element = Feld[x][y];
6082
6083     for (i = 0; i < NUM_BELTS; i++)
6084     {
6085       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6086       {
6087         int e_belt_nr = getBeltNrFromBeltElement(element);
6088         int belt_nr = i;
6089
6090         if (e_belt_nr == belt_nr)
6091         {
6092           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6093
6094           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6095         }
6096       }
6097     }
6098   }
6099 }
6100
6101 static void ToggleBeltSwitch(int x, int y)
6102 {
6103   static int belt_base_element[4] =
6104   {
6105     EL_CONVEYOR_BELT_1_LEFT,
6106     EL_CONVEYOR_BELT_2_LEFT,
6107     EL_CONVEYOR_BELT_3_LEFT,
6108     EL_CONVEYOR_BELT_4_LEFT
6109   };
6110   static int belt_base_active_element[4] =
6111   {
6112     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6113     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6114     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6115     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6116   };
6117   static int belt_base_switch_element[4] =
6118   {
6119     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6120     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6121     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6122     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6123   };
6124   static int belt_move_dir[4] =
6125   {
6126     MV_LEFT,
6127     MV_NONE,
6128     MV_RIGHT,
6129     MV_NONE,
6130   };
6131
6132   int element = Feld[x][y];
6133   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6134   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6135   int belt_dir = belt_move_dir[belt_dir_nr];
6136   int xx, yy, i;
6137
6138   if (!IS_BELT_SWITCH(element))
6139     return;
6140
6141   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6142   game.belt_dir[belt_nr] = belt_dir;
6143
6144   if (belt_dir_nr == 3)
6145     belt_dir_nr = 1;
6146
6147   /* set frame order for belt animation graphic according to belt direction */
6148   for (i = 0; i < NUM_BELT_PARTS; i++)
6149   {
6150     int element = belt_base_active_element[belt_nr] + i;
6151     int graphic_1 = el2img(element);
6152     int graphic_2 = el2panelimg(element);
6153
6154     if (belt_dir == MV_LEFT)
6155     {
6156       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6157       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6158     }
6159     else
6160     {
6161       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6162       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6163     }
6164   }
6165
6166   SCAN_PLAYFIELD(xx, yy)
6167   {
6168     int element = Feld[xx][yy];
6169
6170     if (IS_BELT_SWITCH(element))
6171     {
6172       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6173
6174       if (e_belt_nr == belt_nr)
6175       {
6176         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6177         TEST_DrawLevelField(xx, yy);
6178       }
6179     }
6180     else if (IS_BELT(element) && belt_dir != MV_NONE)
6181     {
6182       int e_belt_nr = getBeltNrFromBeltElement(element);
6183
6184       if (e_belt_nr == belt_nr)
6185       {
6186         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6187
6188         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6189         TEST_DrawLevelField(xx, yy);
6190       }
6191     }
6192     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6193     {
6194       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6195
6196       if (e_belt_nr == belt_nr)
6197       {
6198         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6199
6200         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6201         TEST_DrawLevelField(xx, yy);
6202       }
6203     }
6204   }
6205 }
6206
6207 static void ToggleSwitchgateSwitch(int x, int y)
6208 {
6209   int xx, yy;
6210
6211   game.switchgate_pos = !game.switchgate_pos;
6212
6213   SCAN_PLAYFIELD(xx, yy)
6214   {
6215     int element = Feld[xx][yy];
6216
6217 #if !USE_BOTH_SWITCHGATE_SWITCHES
6218     if (element == EL_SWITCHGATE_SWITCH_UP ||
6219         element == EL_SWITCHGATE_SWITCH_DOWN)
6220     {
6221       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6222       TEST_DrawLevelField(xx, yy);
6223     }
6224     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6225              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6226     {
6227       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6228       TEST_DrawLevelField(xx, yy);
6229     }
6230 #else
6231     if (element == EL_SWITCHGATE_SWITCH_UP)
6232     {
6233       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6234       TEST_DrawLevelField(xx, yy);
6235     }
6236     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6237     {
6238       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6239       TEST_DrawLevelField(xx, yy);
6240     }
6241     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6242     {
6243       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6244       TEST_DrawLevelField(xx, yy);
6245     }
6246     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6247     {
6248       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6249       TEST_DrawLevelField(xx, yy);
6250     }
6251 #endif
6252     else if (element == EL_SWITCHGATE_OPEN ||
6253              element == EL_SWITCHGATE_OPENING)
6254     {
6255       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6256
6257       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6258     }
6259     else if (element == EL_SWITCHGATE_CLOSED ||
6260              element == EL_SWITCHGATE_CLOSING)
6261     {
6262       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6263
6264       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6265     }
6266   }
6267 }
6268
6269 static int getInvisibleActiveFromInvisibleElement(int element)
6270 {
6271   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6272           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6273           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6274           element);
6275 }
6276
6277 static int getInvisibleFromInvisibleActiveElement(int element)
6278 {
6279   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6280           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6281           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6282           element);
6283 }
6284
6285 static void RedrawAllLightSwitchesAndInvisibleElements()
6286 {
6287   int x, y;
6288
6289   SCAN_PLAYFIELD(x, y)
6290   {
6291     int element = Feld[x][y];
6292
6293     if (element == EL_LIGHT_SWITCH &&
6294         game.light_time_left > 0)
6295     {
6296       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6297       TEST_DrawLevelField(x, y);
6298     }
6299     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6300              game.light_time_left == 0)
6301     {
6302       Feld[x][y] = EL_LIGHT_SWITCH;
6303       TEST_DrawLevelField(x, y);
6304     }
6305     else if (element == EL_EMC_DRIPPER &&
6306              game.light_time_left > 0)
6307     {
6308       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6309       TEST_DrawLevelField(x, y);
6310     }
6311     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6312              game.light_time_left == 0)
6313     {
6314       Feld[x][y] = EL_EMC_DRIPPER;
6315       TEST_DrawLevelField(x, y);
6316     }
6317     else if (element == EL_INVISIBLE_STEELWALL ||
6318              element == EL_INVISIBLE_WALL ||
6319              element == EL_INVISIBLE_SAND)
6320     {
6321       if (game.light_time_left > 0)
6322         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6323
6324       TEST_DrawLevelField(x, y);
6325
6326       /* uncrumble neighbour fields, if needed */
6327       if (element == EL_INVISIBLE_SAND)
6328         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6329     }
6330     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6331              element == EL_INVISIBLE_WALL_ACTIVE ||
6332              element == EL_INVISIBLE_SAND_ACTIVE)
6333     {
6334       if (game.light_time_left == 0)
6335         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6336
6337       TEST_DrawLevelField(x, y);
6338
6339       /* re-crumble neighbour fields, if needed */
6340       if (element == EL_INVISIBLE_SAND)
6341         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6342     }
6343   }
6344 }
6345
6346 static void RedrawAllInvisibleElementsForLenses()
6347 {
6348   int x, y;
6349
6350   SCAN_PLAYFIELD(x, y)
6351   {
6352     int element = Feld[x][y];
6353
6354     if (element == EL_EMC_DRIPPER &&
6355         game.lenses_time_left > 0)
6356     {
6357       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6358       TEST_DrawLevelField(x, y);
6359     }
6360     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6361              game.lenses_time_left == 0)
6362     {
6363       Feld[x][y] = EL_EMC_DRIPPER;
6364       TEST_DrawLevelField(x, y);
6365     }
6366     else if (element == EL_INVISIBLE_STEELWALL ||
6367              element == EL_INVISIBLE_WALL ||
6368              element == EL_INVISIBLE_SAND)
6369     {
6370       if (game.lenses_time_left > 0)
6371         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6372
6373       TEST_DrawLevelField(x, y);
6374
6375       /* uncrumble neighbour fields, if needed */
6376       if (element == EL_INVISIBLE_SAND)
6377         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6378     }
6379     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6380              element == EL_INVISIBLE_WALL_ACTIVE ||
6381              element == EL_INVISIBLE_SAND_ACTIVE)
6382     {
6383       if (game.lenses_time_left == 0)
6384         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6385
6386       TEST_DrawLevelField(x, y);
6387
6388       /* re-crumble neighbour fields, if needed */
6389       if (element == EL_INVISIBLE_SAND)
6390         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6391     }
6392   }
6393 }
6394
6395 static void RedrawAllInvisibleElementsForMagnifier()
6396 {
6397   int x, y;
6398
6399   SCAN_PLAYFIELD(x, y)
6400   {
6401     int element = Feld[x][y];
6402
6403     if (element == EL_EMC_FAKE_GRASS &&
6404         game.magnify_time_left > 0)
6405     {
6406       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6407       TEST_DrawLevelField(x, y);
6408     }
6409     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6410              game.magnify_time_left == 0)
6411     {
6412       Feld[x][y] = EL_EMC_FAKE_GRASS;
6413       TEST_DrawLevelField(x, y);
6414     }
6415     else if (IS_GATE_GRAY(element) &&
6416              game.magnify_time_left > 0)
6417     {
6418       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6419                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6420                     IS_EM_GATE_GRAY(element) ?
6421                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6422                     IS_EMC_GATE_GRAY(element) ?
6423                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6424                     element);
6425       TEST_DrawLevelField(x, y);
6426     }
6427     else if (IS_GATE_GRAY_ACTIVE(element) &&
6428              game.magnify_time_left == 0)
6429     {
6430       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6431                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6432                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6433                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6434                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6435                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6436                     element);
6437       TEST_DrawLevelField(x, y);
6438     }
6439   }
6440 }
6441
6442 static void ToggleLightSwitch(int x, int y)
6443 {
6444   int element = Feld[x][y];
6445
6446   game.light_time_left =
6447     (element == EL_LIGHT_SWITCH ?
6448      level.time_light * FRAMES_PER_SECOND : 0);
6449
6450   RedrawAllLightSwitchesAndInvisibleElements();
6451 }
6452
6453 static void ActivateTimegateSwitch(int x, int y)
6454 {
6455   int xx, yy;
6456
6457   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6458
6459   SCAN_PLAYFIELD(xx, yy)
6460   {
6461     int element = Feld[xx][yy];
6462
6463     if (element == EL_TIMEGATE_CLOSED ||
6464         element == EL_TIMEGATE_CLOSING)
6465     {
6466       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6467       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6468     }
6469
6470     /*
6471     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6472     {
6473       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6474       TEST_DrawLevelField(xx, yy);
6475     }
6476     */
6477
6478   }
6479
6480 #if 1
6481   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6482                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6483 #else
6484   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6485 #endif
6486 }
6487
6488 void Impact(int x, int y)
6489 {
6490   boolean last_line = (y == lev_fieldy - 1);
6491   boolean object_hit = FALSE;
6492   boolean impact = (last_line || object_hit);
6493   int element = Feld[x][y];
6494   int smashed = EL_STEELWALL;
6495
6496   if (!last_line)       /* check if element below was hit */
6497   {
6498     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6499       return;
6500
6501     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6502                                          MovDir[x][y + 1] != MV_DOWN ||
6503                                          MovPos[x][y + 1] <= TILEY / 2));
6504
6505     /* do not smash moving elements that left the smashed field in time */
6506     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6507         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6508       object_hit = FALSE;
6509
6510 #if USE_QUICKSAND_IMPACT_BUGFIX
6511     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6512     {
6513       RemoveMovingField(x, y + 1);
6514       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6515       Feld[x][y + 2] = EL_ROCK;
6516       TEST_DrawLevelField(x, y + 2);
6517
6518       object_hit = TRUE;
6519     }
6520
6521     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6522     {
6523       RemoveMovingField(x, y + 1);
6524       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6525       Feld[x][y + 2] = EL_ROCK;
6526       TEST_DrawLevelField(x, y + 2);
6527
6528       object_hit = TRUE;
6529     }
6530 #endif
6531
6532     if (object_hit)
6533       smashed = MovingOrBlocked2Element(x, y + 1);
6534
6535     impact = (last_line || object_hit);
6536   }
6537
6538   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6539   {
6540     SplashAcid(x, y + 1);
6541     return;
6542   }
6543
6544   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6545   /* only reset graphic animation if graphic really changes after impact */
6546   if (impact &&
6547       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6548   {
6549     ResetGfxAnimation(x, y);
6550     TEST_DrawLevelField(x, y);
6551   }
6552
6553   if (impact && CAN_EXPLODE_IMPACT(element))
6554   {
6555     Bang(x, y);
6556     return;
6557   }
6558   else if (impact && element == EL_PEARL &&
6559            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6560   {
6561     ResetGfxAnimation(x, y);
6562
6563     Feld[x][y] = EL_PEARL_BREAKING;
6564     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6565     return;
6566   }
6567   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6568   {
6569     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6570
6571     return;
6572   }
6573
6574   if (impact && element == EL_AMOEBA_DROP)
6575   {
6576     if (object_hit && IS_PLAYER(x, y + 1))
6577       KillPlayerUnlessEnemyProtected(x, y + 1);
6578     else if (object_hit && smashed == EL_PENGUIN)
6579       Bang(x, y + 1);
6580     else
6581     {
6582       Feld[x][y] = EL_AMOEBA_GROWING;
6583       Store[x][y] = EL_AMOEBA_WET;
6584
6585       ResetRandomAnimationValue(x, y);
6586     }
6587     return;
6588   }
6589
6590   if (object_hit)               /* check which object was hit */
6591   {
6592     if ((CAN_PASS_MAGIC_WALL(element) && 
6593          (smashed == EL_MAGIC_WALL ||
6594           smashed == EL_BD_MAGIC_WALL)) ||
6595         (CAN_PASS_DC_MAGIC_WALL(element) &&
6596          smashed == EL_DC_MAGIC_WALL))
6597     {
6598       int xx, yy;
6599       int activated_magic_wall =
6600         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6601          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6602          EL_DC_MAGIC_WALL_ACTIVE);
6603
6604       /* activate magic wall / mill */
6605       SCAN_PLAYFIELD(xx, yy)
6606       {
6607         if (Feld[xx][yy] == smashed)
6608           Feld[xx][yy] = activated_magic_wall;
6609       }
6610
6611       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6612       game.magic_wall_active = TRUE;
6613
6614       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6615                             SND_MAGIC_WALL_ACTIVATING :
6616                             smashed == EL_BD_MAGIC_WALL ?
6617                             SND_BD_MAGIC_WALL_ACTIVATING :
6618                             SND_DC_MAGIC_WALL_ACTIVATING));
6619     }
6620
6621     if (IS_PLAYER(x, y + 1))
6622     {
6623       if (CAN_SMASH_PLAYER(element))
6624       {
6625         KillPlayerUnlessEnemyProtected(x, y + 1);
6626         return;
6627       }
6628     }
6629     else if (smashed == EL_PENGUIN)
6630     {
6631       if (CAN_SMASH_PLAYER(element))
6632       {
6633         Bang(x, y + 1);
6634         return;
6635       }
6636     }
6637     else if (element == EL_BD_DIAMOND)
6638     {
6639       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6640       {
6641         Bang(x, y + 1);
6642         return;
6643       }
6644     }
6645     else if (((element == EL_SP_INFOTRON ||
6646                element == EL_SP_ZONK) &&
6647               (smashed == EL_SP_SNIKSNAK ||
6648                smashed == EL_SP_ELECTRON ||
6649                smashed == EL_SP_DISK_ORANGE)) ||
6650              (element == EL_SP_INFOTRON &&
6651               smashed == EL_SP_DISK_YELLOW))
6652     {
6653       Bang(x, y + 1);
6654       return;
6655     }
6656     else if (CAN_SMASH_EVERYTHING(element))
6657     {
6658       if (IS_CLASSIC_ENEMY(smashed) ||
6659           CAN_EXPLODE_SMASHED(smashed))
6660       {
6661         Bang(x, y + 1);
6662         return;
6663       }
6664       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6665       {
6666         if (smashed == EL_LAMP ||
6667             smashed == EL_LAMP_ACTIVE)
6668         {
6669           Bang(x, y + 1);
6670           return;
6671         }
6672         else if (smashed == EL_NUT)
6673         {
6674           Feld[x][y + 1] = EL_NUT_BREAKING;
6675           PlayLevelSound(x, y, SND_NUT_BREAKING);
6676           RaiseScoreElement(EL_NUT);
6677           return;
6678         }
6679         else if (smashed == EL_PEARL)
6680         {
6681           ResetGfxAnimation(x, y);
6682
6683           Feld[x][y + 1] = EL_PEARL_BREAKING;
6684           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6685           return;
6686         }
6687         else if (smashed == EL_DIAMOND)
6688         {
6689           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6690           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6691           return;
6692         }
6693         else if (IS_BELT_SWITCH(smashed))
6694         {
6695           ToggleBeltSwitch(x, y + 1);
6696         }
6697         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6698                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6699                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6700                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6701         {
6702           ToggleSwitchgateSwitch(x, y + 1);
6703         }
6704         else if (smashed == EL_LIGHT_SWITCH ||
6705                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6706         {
6707           ToggleLightSwitch(x, y + 1);
6708         }
6709         else
6710         {
6711 #if 0
6712           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6713 #endif
6714
6715           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6716
6717           CheckElementChangeBySide(x, y + 1, smashed, element,
6718                                    CE_SWITCHED, CH_SIDE_TOP);
6719           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6720                                             CH_SIDE_TOP);
6721         }
6722       }
6723       else
6724       {
6725         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6726       }
6727     }
6728   }
6729
6730   /* play sound of magic wall / mill */
6731   if (!last_line &&
6732       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6733        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6734        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6735   {
6736     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6737       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6738     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6739       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6740     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6741       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6742
6743     return;
6744   }
6745
6746   /* play sound of object that hits the ground */
6747   if (last_line || object_hit)
6748     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6749 }
6750
6751 inline static void TurnRoundExt(int x, int y)
6752 {
6753   static struct
6754   {
6755     int dx, dy;
6756   } move_xy[] =
6757   {
6758     {  0,  0 },
6759     { -1,  0 },
6760     { +1,  0 },
6761     {  0,  0 },
6762     {  0, -1 },
6763     {  0,  0 }, { 0, 0 }, { 0, 0 },
6764     {  0, +1 }
6765   };
6766   static struct
6767   {
6768     int left, right, back;
6769   } turn[] =
6770   {
6771     { 0,        0,              0        },
6772     { MV_DOWN,  MV_UP,          MV_RIGHT },
6773     { MV_UP,    MV_DOWN,        MV_LEFT  },
6774     { 0,        0,              0        },
6775     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6776     { 0,        0,              0        },
6777     { 0,        0,              0        },
6778     { 0,        0,              0        },
6779     { MV_RIGHT, MV_LEFT,        MV_UP    }
6780   };
6781
6782   int element = Feld[x][y];
6783   int move_pattern = element_info[element].move_pattern;
6784
6785   int old_move_dir = MovDir[x][y];
6786   int left_dir  = turn[old_move_dir].left;
6787   int right_dir = turn[old_move_dir].right;
6788   int back_dir  = turn[old_move_dir].back;
6789
6790   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6791   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6792   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6793   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6794
6795   int left_x  = x + left_dx,  left_y  = y + left_dy;
6796   int right_x = x + right_dx, right_y = y + right_dy;
6797   int move_x  = x + move_dx,  move_y  = y + move_dy;
6798
6799   int xx, yy;
6800
6801   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6802   {
6803     TestIfBadThingTouchesOtherBadThing(x, y);
6804
6805     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6806       MovDir[x][y] = right_dir;
6807     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6808       MovDir[x][y] = left_dir;
6809
6810     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6811       MovDelay[x][y] = 9;
6812     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6813       MovDelay[x][y] = 1;
6814   }
6815   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6816   {
6817     TestIfBadThingTouchesOtherBadThing(x, y);
6818
6819     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6820       MovDir[x][y] = left_dir;
6821     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6822       MovDir[x][y] = right_dir;
6823
6824     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6825       MovDelay[x][y] = 9;
6826     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6827       MovDelay[x][y] = 1;
6828   }
6829   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6830   {
6831     TestIfBadThingTouchesOtherBadThing(x, y);
6832
6833     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6834       MovDir[x][y] = left_dir;
6835     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6836       MovDir[x][y] = right_dir;
6837
6838     if (MovDir[x][y] != old_move_dir)
6839       MovDelay[x][y] = 9;
6840   }
6841   else if (element == EL_YAMYAM)
6842   {
6843     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6844     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6845
6846     if (can_turn_left && can_turn_right)
6847       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6848     else if (can_turn_left)
6849       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6850     else if (can_turn_right)
6851       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6852     else
6853       MovDir[x][y] = back_dir;
6854
6855     MovDelay[x][y] = 16 + 16 * RND(3);
6856   }
6857   else if (element == EL_DARK_YAMYAM)
6858   {
6859     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6860                                                          left_x, left_y);
6861     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6862                                                          right_x, right_y);
6863
6864     if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6866     else if (can_turn_left)
6867       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6868     else if (can_turn_right)
6869       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     MovDelay[x][y] = 16 + 16 * RND(3);
6874   }
6875   else if (element == EL_PACMAN)
6876   {
6877     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6878     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6879
6880     if (can_turn_left && can_turn_right)
6881       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6882     else if (can_turn_left)
6883       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6884     else if (can_turn_right)
6885       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6886     else
6887       MovDir[x][y] = back_dir;
6888
6889     MovDelay[x][y] = 6 + RND(40);
6890   }
6891   else if (element == EL_PIG)
6892   {
6893     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6894     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6895     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6896     boolean should_turn_left, should_turn_right, should_move_on;
6897     int rnd_value = 24;
6898     int rnd = RND(rnd_value);
6899
6900     should_turn_left = (can_turn_left &&
6901                         (!can_move_on ||
6902                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6903                                                    y + back_dy + left_dy)));
6904     should_turn_right = (can_turn_right &&
6905                          (!can_move_on ||
6906                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6907                                                     y + back_dy + right_dy)));
6908     should_move_on = (can_move_on &&
6909                       (!can_turn_left ||
6910                        !can_turn_right ||
6911                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6912                                                  y + move_dy + left_dy) ||
6913                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6914                                                  y + move_dy + right_dy)));
6915
6916     if (should_turn_left || should_turn_right || should_move_on)
6917     {
6918       if (should_turn_left && should_turn_right && should_move_on)
6919         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6920                         rnd < 2 * rnd_value / 3 ? right_dir :
6921                         old_move_dir);
6922       else if (should_turn_left && should_turn_right)
6923         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6924       else if (should_turn_left && should_move_on)
6925         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6926       else if (should_turn_right && should_move_on)
6927         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6928       else if (should_turn_left)
6929         MovDir[x][y] = left_dir;
6930       else if (should_turn_right)
6931         MovDir[x][y] = right_dir;
6932       else if (should_move_on)
6933         MovDir[x][y] = old_move_dir;
6934     }
6935     else if (can_move_on && rnd > rnd_value / 8)
6936       MovDir[x][y] = old_move_dir;
6937     else if (can_turn_left && can_turn_right)
6938       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6939     else if (can_turn_left && rnd > rnd_value / 8)
6940       MovDir[x][y] = left_dir;
6941     else if (can_turn_right && rnd > rnd_value/8)
6942       MovDir[x][y] = right_dir;
6943     else
6944       MovDir[x][y] = back_dir;
6945
6946     xx = x + move_xy[MovDir[x][y]].dx;
6947     yy = y + move_xy[MovDir[x][y]].dy;
6948
6949     if (!IN_LEV_FIELD(xx, yy) ||
6950         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6951       MovDir[x][y] = old_move_dir;
6952
6953     MovDelay[x][y] = 0;
6954   }
6955   else if (element == EL_DRAGON)
6956   {
6957     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6958     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6959     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6960     int rnd_value = 24;
6961     int rnd = RND(rnd_value);
6962
6963     if (can_move_on && rnd > rnd_value / 8)
6964       MovDir[x][y] = old_move_dir;
6965     else if (can_turn_left && can_turn_right)
6966       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6967     else if (can_turn_left && rnd > rnd_value / 8)
6968       MovDir[x][y] = left_dir;
6969     else if (can_turn_right && rnd > rnd_value / 8)
6970       MovDir[x][y] = right_dir;
6971     else
6972       MovDir[x][y] = back_dir;
6973
6974     xx = x + move_xy[MovDir[x][y]].dx;
6975     yy = y + move_xy[MovDir[x][y]].dy;
6976
6977     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6978       MovDir[x][y] = old_move_dir;
6979
6980     MovDelay[x][y] = 0;
6981   }
6982   else if (element == EL_MOLE)
6983   {
6984     boolean can_move_on =
6985       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6986                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6987                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6988     if (!can_move_on)
6989     {
6990       boolean can_turn_left =
6991         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6992                               IS_AMOEBOID(Feld[left_x][left_y])));
6993
6994       boolean can_turn_right =
6995         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6996                               IS_AMOEBOID(Feld[right_x][right_y])));
6997
6998       if (can_turn_left && can_turn_right)
6999         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7000       else if (can_turn_left)
7001         MovDir[x][y] = left_dir;
7002       else
7003         MovDir[x][y] = right_dir;
7004     }
7005
7006     if (MovDir[x][y] != old_move_dir)
7007       MovDelay[x][y] = 9;
7008   }
7009   else if (element == EL_BALLOON)
7010   {
7011     MovDir[x][y] = game.wind_direction;
7012     MovDelay[x][y] = 0;
7013   }
7014   else if (element == EL_SPRING)
7015   {
7016 #if USE_NEW_SPRING_BUMPER
7017     if (MovDir[x][y] & MV_HORIZONTAL)
7018     {
7019       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7020           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7021       {
7022         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7023         ResetGfxAnimation(move_x, move_y);
7024         TEST_DrawLevelField(move_x, move_y);
7025
7026         MovDir[x][y] = back_dir;
7027       }
7028       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7029                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7030         MovDir[x][y] = MV_NONE;
7031     }
7032 #else
7033     if (MovDir[x][y] & MV_HORIZONTAL &&
7034         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7035          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7036       MovDir[x][y] = MV_NONE;
7037 #endif
7038
7039     MovDelay[x][y] = 0;
7040   }
7041   else if (element == EL_ROBOT ||
7042            element == EL_SATELLITE ||
7043            element == EL_PENGUIN ||
7044            element == EL_EMC_ANDROID)
7045   {
7046     int attr_x = -1, attr_y = -1;
7047
7048     if (AllPlayersGone)
7049     {
7050       attr_x = ExitX;
7051       attr_y = ExitY;
7052     }
7053     else
7054     {
7055       int i;
7056
7057       for (i = 0; i < MAX_PLAYERS; i++)
7058       {
7059         struct PlayerInfo *player = &stored_player[i];
7060         int jx = player->jx, jy = player->jy;
7061
7062         if (!player->active)
7063           continue;
7064
7065         if (attr_x == -1 ||
7066             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7067         {
7068           attr_x = jx;
7069           attr_y = jy;
7070         }
7071       }
7072     }
7073
7074     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7075         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7076          game.engine_version < VERSION_IDENT(3,1,0,0)))
7077     {
7078       attr_x = ZX;
7079       attr_y = ZY;
7080     }
7081
7082     if (element == EL_PENGUIN)
7083     {
7084       int i;
7085       static int xy[4][2] =
7086       {
7087         { 0, -1 },
7088         { -1, 0 },
7089         { +1, 0 },
7090         { 0, +1 }
7091       };
7092
7093       for (i = 0; i < NUM_DIRECTIONS; i++)
7094       {
7095         int ex = x + xy[i][0];
7096         int ey = y + xy[i][1];
7097
7098         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7099                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7100                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7101                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7102         {
7103           attr_x = ex;
7104           attr_y = ey;
7105           break;
7106         }
7107       }
7108     }
7109
7110     MovDir[x][y] = MV_NONE;
7111     if (attr_x < x)
7112       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7113     else if (attr_x > x)
7114       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7115     if (attr_y < y)
7116       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7117     else if (attr_y > y)
7118       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7119
7120     if (element == EL_ROBOT)
7121     {
7122       int newx, newy;
7123
7124       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7125         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7126       Moving2Blocked(x, y, &newx, &newy);
7127
7128       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7129         MovDelay[x][y] = 8 + 8 * !RND(3);
7130       else
7131         MovDelay[x][y] = 16;
7132     }
7133     else if (element == EL_PENGUIN)
7134     {
7135       int newx, newy;
7136
7137       MovDelay[x][y] = 1;
7138
7139       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7140       {
7141         boolean first_horiz = RND(2);
7142         int new_move_dir = MovDir[x][y];
7143
7144         MovDir[x][y] =
7145           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7146         Moving2Blocked(x, y, &newx, &newy);
7147
7148         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7149           return;
7150
7151         MovDir[x][y] =
7152           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7153         Moving2Blocked(x, y, &newx, &newy);
7154
7155         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7156           return;
7157
7158         MovDir[x][y] = old_move_dir;
7159         return;
7160       }
7161     }
7162     else if (element == EL_SATELLITE)
7163     {
7164       int newx, newy;
7165
7166       MovDelay[x][y] = 1;
7167
7168       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7169       {
7170         boolean first_horiz = RND(2);
7171         int new_move_dir = MovDir[x][y];
7172
7173         MovDir[x][y] =
7174           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7175         Moving2Blocked(x, y, &newx, &newy);
7176
7177         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7178           return;
7179
7180         MovDir[x][y] =
7181           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7182         Moving2Blocked(x, y, &newx, &newy);
7183
7184         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7185           return;
7186
7187         MovDir[x][y] = old_move_dir;
7188         return;
7189       }
7190     }
7191     else if (element == EL_EMC_ANDROID)
7192     {
7193       static int check_pos[16] =
7194       {
7195         -1,             /*  0 => (invalid)          */
7196         7,              /*  1 => MV_LEFT            */
7197         3,              /*  2 => MV_RIGHT           */
7198         -1,             /*  3 => (invalid)          */
7199         1,              /*  4 =>            MV_UP   */
7200         0,              /*  5 => MV_LEFT  | MV_UP   */
7201         2,              /*  6 => MV_RIGHT | MV_UP   */
7202         -1,             /*  7 => (invalid)          */
7203         5,              /*  8 =>            MV_DOWN */
7204         6,              /*  9 => MV_LEFT  | MV_DOWN */
7205         4,              /* 10 => MV_RIGHT | MV_DOWN */
7206         -1,             /* 11 => (invalid)          */
7207         -1,             /* 12 => (invalid)          */
7208         -1,             /* 13 => (invalid)          */
7209         -1,             /* 14 => (invalid)          */
7210         -1,             /* 15 => (invalid)          */
7211       };
7212       static struct
7213       {
7214         int dx, dy;
7215         int dir;
7216       } check_xy[8] =
7217       {
7218         { -1, -1,       MV_LEFT  | MV_UP   },
7219         {  0, -1,                  MV_UP   },
7220         { +1, -1,       MV_RIGHT | MV_UP   },
7221         { +1,  0,       MV_RIGHT           },
7222         { +1, +1,       MV_RIGHT | MV_DOWN },
7223         {  0, +1,                  MV_DOWN },
7224         { -1, +1,       MV_LEFT  | MV_DOWN },
7225         { -1,  0,       MV_LEFT            },
7226       };
7227       int start_pos, check_order;
7228       boolean can_clone = FALSE;
7229       int i;
7230
7231       /* check if there is any free field around current position */
7232       for (i = 0; i < 8; i++)
7233       {
7234         int newx = x + check_xy[i].dx;
7235         int newy = y + check_xy[i].dy;
7236
7237         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7238         {
7239           can_clone = TRUE;
7240
7241           break;
7242         }
7243       }
7244
7245       if (can_clone)            /* randomly find an element to clone */
7246       {
7247         can_clone = FALSE;
7248
7249         start_pos = check_pos[RND(8)];
7250         check_order = (RND(2) ? -1 : +1);
7251
7252         for (i = 0; i < 8; i++)
7253         {
7254           int pos_raw = start_pos + i * check_order;
7255           int pos = (pos_raw + 8) % 8;
7256           int newx = x + check_xy[pos].dx;
7257           int newy = y + check_xy[pos].dy;
7258
7259           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7260           {
7261             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7262             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7263
7264             Store[x][y] = Feld[newx][newy];
7265
7266             can_clone = TRUE;
7267
7268             break;
7269           }
7270         }
7271       }
7272
7273       if (can_clone)            /* randomly find a direction to move */
7274       {
7275         can_clone = FALSE;
7276
7277         start_pos = check_pos[RND(8)];
7278         check_order = (RND(2) ? -1 : +1);
7279
7280         for (i = 0; i < 8; i++)
7281         {
7282           int pos_raw = start_pos + i * check_order;
7283           int pos = (pos_raw + 8) % 8;
7284           int newx = x + check_xy[pos].dx;
7285           int newy = y + check_xy[pos].dy;
7286           int new_move_dir = check_xy[pos].dir;
7287
7288           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7289           {
7290             MovDir[x][y] = new_move_dir;
7291             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7292
7293             can_clone = TRUE;
7294
7295             break;
7296           }
7297         }
7298       }
7299
7300       if (can_clone)            /* cloning and moving successful */
7301         return;
7302
7303       /* cannot clone -- try to move towards player */
7304
7305       start_pos = check_pos[MovDir[x][y] & 0x0f];
7306       check_order = (RND(2) ? -1 : +1);
7307
7308       for (i = 0; i < 3; i++)
7309       {
7310         /* first check start_pos, then previous/next or (next/previous) pos */
7311         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7312         int pos = (pos_raw + 8) % 8;
7313         int newx = x + check_xy[pos].dx;
7314         int newy = y + check_xy[pos].dy;
7315         int new_move_dir = check_xy[pos].dir;
7316
7317         if (IS_PLAYER(newx, newy))
7318           break;
7319
7320         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7321         {
7322           MovDir[x][y] = new_move_dir;
7323           MovDelay[x][y] = level.android_move_time * 8 + 1;
7324
7325           break;
7326         }
7327       }
7328     }
7329   }
7330   else if (move_pattern == MV_TURNING_LEFT ||
7331            move_pattern == MV_TURNING_RIGHT ||
7332            move_pattern == MV_TURNING_LEFT_RIGHT ||
7333            move_pattern == MV_TURNING_RIGHT_LEFT ||
7334            move_pattern == MV_TURNING_RANDOM ||
7335            move_pattern == MV_ALL_DIRECTIONS)
7336   {
7337     boolean can_turn_left =
7338       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7339     boolean can_turn_right =
7340       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7341
7342     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7343       return;
7344
7345     if (move_pattern == MV_TURNING_LEFT)
7346       MovDir[x][y] = left_dir;
7347     else if (move_pattern == MV_TURNING_RIGHT)
7348       MovDir[x][y] = right_dir;
7349     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7350       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7351     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7352       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7353     else if (move_pattern == MV_TURNING_RANDOM)
7354       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7355                       can_turn_right && !can_turn_left ? right_dir :
7356                       RND(2) ? left_dir : right_dir);
7357     else if (can_turn_left && can_turn_right)
7358       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7359     else if (can_turn_left)
7360       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7361     else if (can_turn_right)
7362       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7363     else
7364       MovDir[x][y] = back_dir;
7365
7366     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7367   }
7368   else if (move_pattern == MV_HORIZONTAL ||
7369            move_pattern == MV_VERTICAL)
7370   {
7371     if (move_pattern & old_move_dir)
7372       MovDir[x][y] = back_dir;
7373     else if (move_pattern == MV_HORIZONTAL)
7374       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7375     else if (move_pattern == MV_VERTICAL)
7376       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7377
7378     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7379   }
7380   else if (move_pattern & MV_ANY_DIRECTION)
7381   {
7382     MovDir[x][y] = move_pattern;
7383     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7384   }
7385   else if (move_pattern & MV_WIND_DIRECTION)
7386   {
7387     MovDir[x][y] = game.wind_direction;
7388     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7389   }
7390   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7391   {
7392     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7393       MovDir[x][y] = left_dir;
7394     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7395       MovDir[x][y] = right_dir;
7396
7397     if (MovDir[x][y] != old_move_dir)
7398       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399   }
7400   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7401   {
7402     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7403       MovDir[x][y] = right_dir;
7404     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7405       MovDir[x][y] = left_dir;
7406
7407     if (MovDir[x][y] != old_move_dir)
7408       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7409   }
7410   else if (move_pattern == MV_TOWARDS_PLAYER ||
7411            move_pattern == MV_AWAY_FROM_PLAYER)
7412   {
7413     int attr_x = -1, attr_y = -1;
7414     int newx, newy;
7415     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7416
7417     if (AllPlayersGone)
7418     {
7419       attr_x = ExitX;
7420       attr_y = ExitY;
7421     }
7422     else
7423     {
7424       int i;
7425
7426       for (i = 0; i < MAX_PLAYERS; i++)
7427       {
7428         struct PlayerInfo *player = &stored_player[i];
7429         int jx = player->jx, jy = player->jy;
7430
7431         if (!player->active)
7432           continue;
7433
7434         if (attr_x == -1 ||
7435             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7436         {
7437           attr_x = jx;
7438           attr_y = jy;
7439         }
7440       }
7441     }
7442
7443     MovDir[x][y] = MV_NONE;
7444     if (attr_x < x)
7445       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7446     else if (attr_x > x)
7447       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7448     if (attr_y < y)
7449       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7450     else if (attr_y > y)
7451       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7452
7453     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7454
7455     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7456     {
7457       boolean first_horiz = RND(2);
7458       int new_move_dir = MovDir[x][y];
7459
7460       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7461       {
7462         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7463         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7464
7465         return;
7466       }
7467
7468       MovDir[x][y] =
7469         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7470       Moving2Blocked(x, y, &newx, &newy);
7471
7472       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7473         return;
7474
7475       MovDir[x][y] =
7476         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7477       Moving2Blocked(x, y, &newx, &newy);
7478
7479       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7480         return;
7481
7482       MovDir[x][y] = old_move_dir;
7483     }
7484   }
7485   else if (move_pattern == MV_WHEN_PUSHED ||
7486            move_pattern == MV_WHEN_DROPPED)
7487   {
7488     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7489       MovDir[x][y] = MV_NONE;
7490
7491     MovDelay[x][y] = 0;
7492   }
7493   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7494   {
7495     static int test_xy[7][2] =
7496     {
7497       { 0, -1 },
7498       { -1, 0 },
7499       { +1, 0 },
7500       { 0, +1 },
7501       { 0, -1 },
7502       { -1, 0 },
7503       { +1, 0 },
7504     };
7505     static int test_dir[7] =
7506     {
7507       MV_UP,
7508       MV_LEFT,
7509       MV_RIGHT,
7510       MV_DOWN,
7511       MV_UP,
7512       MV_LEFT,
7513       MV_RIGHT,
7514     };
7515     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7516     int move_preference = -1000000;     /* start with very low preference */
7517     int new_move_dir = MV_NONE;
7518     int start_test = RND(4);
7519     int i;
7520
7521     for (i = 0; i < NUM_DIRECTIONS; i++)
7522     {
7523       int move_dir = test_dir[start_test + i];
7524       int move_dir_preference;
7525
7526       xx = x + test_xy[start_test + i][0];
7527       yy = y + test_xy[start_test + i][1];
7528
7529       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7530           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7531       {
7532         new_move_dir = move_dir;
7533
7534         break;
7535       }
7536
7537       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7538         continue;
7539
7540       move_dir_preference = -1 * RunnerVisit[xx][yy];
7541       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7542         move_dir_preference = PlayerVisit[xx][yy];
7543
7544       if (move_dir_preference > move_preference)
7545       {
7546         /* prefer field that has not been visited for the longest time */
7547         move_preference = move_dir_preference;
7548         new_move_dir = move_dir;
7549       }
7550       else if (move_dir_preference == move_preference &&
7551                move_dir == old_move_dir)
7552       {
7553         /* prefer last direction when all directions are preferred equally */
7554         move_preference = move_dir_preference;
7555         new_move_dir = move_dir;
7556       }
7557     }
7558
7559     MovDir[x][y] = new_move_dir;
7560     if (old_move_dir != new_move_dir)
7561       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7562   }
7563 }
7564
7565 static void TurnRound(int x, int y)
7566 {
7567   int direction = MovDir[x][y];
7568
7569   TurnRoundExt(x, y);
7570
7571   GfxDir[x][y] = MovDir[x][y];
7572
7573   if (direction != MovDir[x][y])
7574     GfxFrame[x][y] = 0;
7575
7576   if (MovDelay[x][y])
7577     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7578
7579   ResetGfxFrame(x, y, FALSE);
7580 }
7581
7582 static boolean JustBeingPushed(int x, int y)
7583 {
7584   int i;
7585
7586   for (i = 0; i < MAX_PLAYERS; i++)
7587   {
7588     struct PlayerInfo *player = &stored_player[i];
7589
7590     if (player->active && player->is_pushing && player->MovPos)
7591     {
7592       int next_jx = player->jx + (player->jx - player->last_jx);
7593       int next_jy = player->jy + (player->jy - player->last_jy);
7594
7595       if (x == next_jx && y == next_jy)
7596         return TRUE;
7597     }
7598   }
7599
7600   return FALSE;
7601 }
7602
7603 void StartMoving(int x, int y)
7604 {
7605   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7606   int element = Feld[x][y];
7607
7608   if (Stop[x][y])
7609     return;
7610
7611   if (MovDelay[x][y] == 0)
7612     GfxAction[x][y] = ACTION_DEFAULT;
7613
7614   if (CAN_FALL(element) && y < lev_fieldy - 1)
7615   {
7616     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7617         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7618       if (JustBeingPushed(x, y))
7619         return;
7620
7621     if (element == EL_QUICKSAND_FULL)
7622     {
7623       if (IS_FREE(x, y + 1))
7624       {
7625         InitMovingField(x, y, MV_DOWN);
7626         started_moving = TRUE;
7627
7628         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7629 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7630         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7631           Store[x][y] = EL_ROCK;
7632 #else
7633         Store[x][y] = EL_ROCK;
7634 #endif
7635
7636         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7637       }
7638       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7639       {
7640         if (!MovDelay[x][y])
7641         {
7642           MovDelay[x][y] = TILEY + 1;
7643
7644           ResetGfxAnimation(x, y);
7645           ResetGfxAnimation(x, y + 1);
7646         }
7647
7648         if (MovDelay[x][y])
7649         {
7650           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7651           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7652
7653           MovDelay[x][y]--;
7654           if (MovDelay[x][y])
7655             return;
7656         }
7657
7658         Feld[x][y] = EL_QUICKSAND_EMPTY;
7659         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7660         Store[x][y + 1] = Store[x][y];
7661         Store[x][y] = 0;
7662
7663         PlayLevelSoundAction(x, y, ACTION_FILLING);
7664       }
7665       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7666       {
7667         if (!MovDelay[x][y])
7668         {
7669           MovDelay[x][y] = TILEY + 1;
7670
7671           ResetGfxAnimation(x, y);
7672           ResetGfxAnimation(x, y + 1);
7673         }
7674
7675         if (MovDelay[x][y])
7676         {
7677           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7678           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7679
7680           MovDelay[x][y]--;
7681           if (MovDelay[x][y])
7682             return;
7683         }
7684
7685         Feld[x][y] = EL_QUICKSAND_EMPTY;
7686         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7687         Store[x][y + 1] = Store[x][y];
7688         Store[x][y] = 0;
7689
7690         PlayLevelSoundAction(x, y, ACTION_FILLING);
7691       }
7692     }
7693     else if (element == EL_QUICKSAND_FAST_FULL)
7694     {
7695       if (IS_FREE(x, y + 1))
7696       {
7697         InitMovingField(x, y, MV_DOWN);
7698         started_moving = TRUE;
7699
7700         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7701 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7702         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7703           Store[x][y] = EL_ROCK;
7704 #else
7705         Store[x][y] = EL_ROCK;
7706 #endif
7707
7708         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7709       }
7710       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7711       {
7712         if (!MovDelay[x][y])
7713         {
7714           MovDelay[x][y] = TILEY + 1;
7715
7716           ResetGfxAnimation(x, y);
7717           ResetGfxAnimation(x, y + 1);
7718         }
7719
7720         if (MovDelay[x][y])
7721         {
7722           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7723           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7724
7725           MovDelay[x][y]--;
7726           if (MovDelay[x][y])
7727             return;
7728         }
7729
7730         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7731         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7732         Store[x][y + 1] = Store[x][y];
7733         Store[x][y] = 0;
7734
7735         PlayLevelSoundAction(x, y, ACTION_FILLING);
7736       }
7737       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7738       {
7739         if (!MovDelay[x][y])
7740         {
7741           MovDelay[x][y] = TILEY + 1;
7742
7743           ResetGfxAnimation(x, y);
7744           ResetGfxAnimation(x, y + 1);
7745         }
7746
7747         if (MovDelay[x][y])
7748         {
7749           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7750           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7751
7752           MovDelay[x][y]--;
7753           if (MovDelay[x][y])
7754             return;
7755         }
7756
7757         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7758         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7759         Store[x][y + 1] = Store[x][y];
7760         Store[x][y] = 0;
7761
7762         PlayLevelSoundAction(x, y, ACTION_FILLING);
7763       }
7764     }
7765     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7766              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7767     {
7768       InitMovingField(x, y, MV_DOWN);
7769       started_moving = TRUE;
7770
7771       Feld[x][y] = EL_QUICKSAND_FILLING;
7772       Store[x][y] = element;
7773
7774       PlayLevelSoundAction(x, y, ACTION_FILLING);
7775     }
7776     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7777              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7778     {
7779       InitMovingField(x, y, MV_DOWN);
7780       started_moving = TRUE;
7781
7782       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7783       Store[x][y] = element;
7784
7785       PlayLevelSoundAction(x, y, ACTION_FILLING);
7786     }
7787     else if (element == EL_MAGIC_WALL_FULL)
7788     {
7789       if (IS_FREE(x, y + 1))
7790       {
7791         InitMovingField(x, y, MV_DOWN);
7792         started_moving = TRUE;
7793
7794         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7795         Store[x][y] = EL_CHANGED(Store[x][y]);
7796       }
7797       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7798       {
7799         if (!MovDelay[x][y])
7800           MovDelay[x][y] = TILEY/4 + 1;
7801
7802         if (MovDelay[x][y])
7803         {
7804           MovDelay[x][y]--;
7805           if (MovDelay[x][y])
7806             return;
7807         }
7808
7809         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7810         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7811         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7812         Store[x][y] = 0;
7813       }
7814     }
7815     else if (element == EL_BD_MAGIC_WALL_FULL)
7816     {
7817       if (IS_FREE(x, y + 1))
7818       {
7819         InitMovingField(x, y, MV_DOWN);
7820         started_moving = TRUE;
7821
7822         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7823         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7824       }
7825       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7826       {
7827         if (!MovDelay[x][y])
7828           MovDelay[x][y] = TILEY/4 + 1;
7829
7830         if (MovDelay[x][y])
7831         {
7832           MovDelay[x][y]--;
7833           if (MovDelay[x][y])
7834             return;
7835         }
7836
7837         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7838         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7839         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7840         Store[x][y] = 0;
7841       }
7842     }
7843     else if (element == EL_DC_MAGIC_WALL_FULL)
7844     {
7845       if (IS_FREE(x, y + 1))
7846       {
7847         InitMovingField(x, y, MV_DOWN);
7848         started_moving = TRUE;
7849
7850         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7851         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7852       }
7853       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7854       {
7855         if (!MovDelay[x][y])
7856           MovDelay[x][y] = TILEY/4 + 1;
7857
7858         if (MovDelay[x][y])
7859         {
7860           MovDelay[x][y]--;
7861           if (MovDelay[x][y])
7862             return;
7863         }
7864
7865         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7866         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7867         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7868         Store[x][y] = 0;
7869       }
7870     }
7871     else if ((CAN_PASS_MAGIC_WALL(element) &&
7872               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7873                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7874              (CAN_PASS_DC_MAGIC_WALL(element) &&
7875               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7876
7877     {
7878       InitMovingField(x, y, MV_DOWN);
7879       started_moving = TRUE;
7880
7881       Feld[x][y] =
7882         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7883          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7884          EL_DC_MAGIC_WALL_FILLING);
7885       Store[x][y] = element;
7886     }
7887     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7888     {
7889       SplashAcid(x, y + 1);
7890
7891       InitMovingField(x, y, MV_DOWN);
7892       started_moving = TRUE;
7893
7894       Store[x][y] = EL_ACID;
7895     }
7896     else if (
7897 #if USE_FIX_IMPACT_COLLISION
7898              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7899               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7900 #else
7901              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7902               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7903 #endif
7904              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7905               CAN_FALL(element) && WasJustFalling[x][y] &&
7906               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7907
7908              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7909               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7910               (Feld[x][y + 1] == EL_BLOCKED)))
7911     {
7912       /* this is needed for a special case not covered by calling "Impact()"
7913          from "ContinueMoving()": if an element moves to a tile directly below
7914          another element which was just falling on that tile (which was empty
7915          in the previous frame), the falling element above would just stop
7916          instead of smashing the element below (in previous version, the above
7917          element was just checked for "moving" instead of "falling", resulting
7918          in incorrect smashes caused by horizontal movement of the above
7919          element; also, the case of the player being the element to smash was
7920          simply not covered here... :-/ ) */
7921
7922       CheckCollision[x][y] = 0;
7923       CheckImpact[x][y] = 0;
7924
7925       Impact(x, y);
7926     }
7927     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7928     {
7929       if (MovDir[x][y] == MV_NONE)
7930       {
7931         InitMovingField(x, y, MV_DOWN);
7932         started_moving = TRUE;
7933       }
7934     }
7935     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7936     {
7937       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7938         MovDir[x][y] = MV_DOWN;
7939
7940       InitMovingField(x, y, MV_DOWN);
7941       started_moving = TRUE;
7942     }
7943     else if (element == EL_AMOEBA_DROP)
7944     {
7945       Feld[x][y] = EL_AMOEBA_GROWING;
7946       Store[x][y] = EL_AMOEBA_WET;
7947     }
7948     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7949               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7950              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7951              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7952     {
7953       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7954                                 (IS_FREE(x - 1, y + 1) ||
7955                                  Feld[x - 1][y + 1] == EL_ACID));
7956       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7957                                 (IS_FREE(x + 1, y + 1) ||
7958                                  Feld[x + 1][y + 1] == EL_ACID));
7959       boolean can_fall_any  = (can_fall_left || can_fall_right);
7960       boolean can_fall_both = (can_fall_left && can_fall_right);
7961       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7962
7963 #if USE_NEW_ALL_SLIPPERY
7964       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7965       {
7966         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7967           can_fall_right = FALSE;
7968         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7969           can_fall_left = FALSE;
7970         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7971           can_fall_right = FALSE;
7972         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7973           can_fall_left = FALSE;
7974
7975         can_fall_any  = (can_fall_left || can_fall_right);
7976         can_fall_both = FALSE;
7977       }
7978 #else
7979       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7980       {
7981         if (slippery_type == SLIPPERY_ONLY_LEFT)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7984           can_fall_left = FALSE;
7985         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7988           can_fall_left = FALSE;
7989
7990         can_fall_any  = (can_fall_left || can_fall_right);
7991         can_fall_both = (can_fall_left && can_fall_right);
7992       }
7993 #endif
7994
7995 #if USE_NEW_ALL_SLIPPERY
7996 #else
7997 #if USE_NEW_SP_SLIPPERY
7998       /* !!! better use the same properties as for custom elements here !!! */
7999       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8000                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8001       {
8002         can_fall_right = FALSE;         /* slip down on left side */
8003         can_fall_both = FALSE;
8004       }
8005 #endif
8006 #endif
8007
8008 #if USE_NEW_ALL_SLIPPERY
8009       if (can_fall_both)
8010       {
8011         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8012           can_fall_right = FALSE;       /* slip down on left side */
8013         else
8014           can_fall_left = !(can_fall_right = RND(2));
8015
8016         can_fall_both = FALSE;
8017       }
8018 #else
8019       if (can_fall_both)
8020       {
8021         if (game.emulation == EMU_BOULDERDASH ||
8022             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8023           can_fall_right = FALSE;       /* slip down on left side */
8024         else
8025           can_fall_left = !(can_fall_right = RND(2));
8026
8027         can_fall_both = FALSE;
8028       }
8029 #endif
8030
8031       if (can_fall_any)
8032       {
8033         /* if not determined otherwise, prefer left side for slipping down */
8034         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8035         started_moving = TRUE;
8036       }
8037     }
8038 #if 0
8039     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8040 #else
8041     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8042 #endif
8043     {
8044       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8045       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8046       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8047       int belt_dir = game.belt_dir[belt_nr];
8048
8049       if ((belt_dir == MV_LEFT  && left_is_free) ||
8050           (belt_dir == MV_RIGHT && right_is_free))
8051       {
8052         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8053
8054         InitMovingField(x, y, belt_dir);
8055         started_moving = TRUE;
8056
8057         Pushed[x][y] = TRUE;
8058         Pushed[nextx][y] = TRUE;
8059
8060         GfxAction[x][y] = ACTION_DEFAULT;
8061       }
8062       else
8063       {
8064         MovDir[x][y] = 0;       /* if element was moving, stop it */
8065       }
8066     }
8067   }
8068
8069   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8070 #if 0
8071   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8072 #else
8073   if (CAN_MOVE(element) && !started_moving)
8074 #endif
8075   {
8076     int move_pattern = element_info[element].move_pattern;
8077     int newx, newy;
8078
8079 #if 0
8080 #if DEBUG
8081     if (MovDir[x][y] == MV_NONE)
8082     {
8083       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8084              x, y, element, element_info[element].token_name);
8085       printf("StartMoving(): This should never happen!\n");
8086     }
8087 #endif
8088 #endif
8089
8090     Moving2Blocked(x, y, &newx, &newy);
8091
8092     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8093       return;
8094
8095     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8096         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8097     {
8098       WasJustMoving[x][y] = 0;
8099       CheckCollision[x][y] = 0;
8100
8101       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8102
8103       if (Feld[x][y] != element)        /* element has changed */
8104         return;
8105     }
8106
8107     if (!MovDelay[x][y])        /* start new movement phase */
8108     {
8109       /* all objects that can change their move direction after each step
8110          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8111
8112       if (element != EL_YAMYAM &&
8113           element != EL_DARK_YAMYAM &&
8114           element != EL_PACMAN &&
8115           !(move_pattern & MV_ANY_DIRECTION) &&
8116           move_pattern != MV_TURNING_LEFT &&
8117           move_pattern != MV_TURNING_RIGHT &&
8118           move_pattern != MV_TURNING_LEFT_RIGHT &&
8119           move_pattern != MV_TURNING_RIGHT_LEFT &&
8120           move_pattern != MV_TURNING_RANDOM)
8121       {
8122         TurnRound(x, y);
8123
8124         if (MovDelay[x][y] && (element == EL_BUG ||
8125                                element == EL_SPACESHIP ||
8126                                element == EL_SP_SNIKSNAK ||
8127                                element == EL_SP_ELECTRON ||
8128                                element == EL_MOLE))
8129           TEST_DrawLevelField(x, y);
8130       }
8131     }
8132
8133     if (MovDelay[x][y])         /* wait some time before next movement */
8134     {
8135       MovDelay[x][y]--;
8136
8137       if (element == EL_ROBOT ||
8138           element == EL_YAMYAM ||
8139           element == EL_DARK_YAMYAM)
8140       {
8141         DrawLevelElementAnimationIfNeeded(x, y, element);
8142         PlayLevelSoundAction(x, y, ACTION_WAITING);
8143       }
8144       else if (element == EL_SP_ELECTRON)
8145         DrawLevelElementAnimationIfNeeded(x, y, element);
8146       else if (element == EL_DRAGON)
8147       {
8148         int i;
8149         int dir = MovDir[x][y];
8150         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8151         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8152         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8153                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8154                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8155                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8156         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8157
8158         GfxAction[x][y] = ACTION_ATTACKING;
8159
8160         if (IS_PLAYER(x, y))
8161           DrawPlayerField(x, y);
8162         else
8163           TEST_DrawLevelField(x, y);
8164
8165         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8166
8167         for (i = 1; i <= 3; i++)
8168         {
8169           int xx = x + i * dx;
8170           int yy = y + i * dy;
8171           int sx = SCREENX(xx);
8172           int sy = SCREENY(yy);
8173           int flame_graphic = graphic + (i - 1);
8174
8175           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8176             break;
8177
8178           if (MovDelay[x][y])
8179           {
8180             int flamed = MovingOrBlocked2Element(xx, yy);
8181
8182             /* !!! */
8183 #if 0
8184             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8185               Bang(xx, yy);
8186             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8187               RemoveMovingField(xx, yy);
8188             else
8189               RemoveField(xx, yy);
8190 #else
8191             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8192               Bang(xx, yy);
8193             else
8194               RemoveMovingField(xx, yy);
8195 #endif
8196
8197             ChangeDelay[xx][yy] = 0;
8198
8199             Feld[xx][yy] = EL_FLAMES;
8200
8201             if (IN_SCR_FIELD(sx, sy))
8202             {
8203               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8204               DrawGraphic(sx, sy, flame_graphic, frame);
8205             }
8206           }
8207           else
8208           {
8209             if (Feld[xx][yy] == EL_FLAMES)
8210               Feld[xx][yy] = EL_EMPTY;
8211             TEST_DrawLevelField(xx, yy);
8212           }
8213         }
8214       }
8215
8216       if (MovDelay[x][y])       /* element still has to wait some time */
8217       {
8218         PlayLevelSoundAction(x, y, ACTION_WAITING);
8219
8220         return;
8221       }
8222     }
8223
8224     /* now make next step */
8225
8226     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8227
8228     if (DONT_COLLIDE_WITH(element) &&
8229         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8230         !PLAYER_ENEMY_PROTECTED(newx, newy))
8231     {
8232       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8233
8234       return;
8235     }
8236
8237     else if (CAN_MOVE_INTO_ACID(element) &&
8238              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8239              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8240              (MovDir[x][y] == MV_DOWN ||
8241               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8242     {
8243       SplashAcid(newx, newy);
8244       Store[x][y] = EL_ACID;
8245     }
8246     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8247     {
8248       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8249           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8250           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8251           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8252       {
8253         RemoveField(x, y);
8254         TEST_DrawLevelField(x, y);
8255
8256         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8257         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8258           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8259
8260         local_player->friends_still_needed--;
8261         if (!local_player->friends_still_needed &&
8262             !local_player->GameOver && AllPlayersGone)
8263           PlayerWins(local_player);
8264
8265         return;
8266       }
8267       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8268       {
8269         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8270           TEST_DrawLevelField(newx, newy);
8271         else
8272           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8273       }
8274       else if (!IS_FREE(newx, newy))
8275       {
8276         GfxAction[x][y] = ACTION_WAITING;
8277
8278         if (IS_PLAYER(x, y))
8279           DrawPlayerField(x, y);
8280         else
8281           TEST_DrawLevelField(x, y);
8282
8283         return;
8284       }
8285     }
8286     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8287     {
8288       if (IS_FOOD_PIG(Feld[newx][newy]))
8289       {
8290         if (IS_MOVING(newx, newy))
8291           RemoveMovingField(newx, newy);
8292         else
8293         {
8294           Feld[newx][newy] = EL_EMPTY;
8295           TEST_DrawLevelField(newx, newy);
8296         }
8297
8298         PlayLevelSound(x, y, SND_PIG_DIGGING);
8299       }
8300       else if (!IS_FREE(newx, newy))
8301       {
8302         if (IS_PLAYER(x, y))
8303           DrawPlayerField(x, y);
8304         else
8305           TEST_DrawLevelField(x, y);
8306
8307         return;
8308       }
8309     }
8310     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8311     {
8312       if (Store[x][y] != EL_EMPTY)
8313       {
8314         boolean can_clone = FALSE;
8315         int xx, yy;
8316
8317         /* check if element to clone is still there */
8318         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8319         {
8320           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8321           {
8322             can_clone = TRUE;
8323
8324             break;
8325           }
8326         }
8327
8328         /* cannot clone or target field not free anymore -- do not clone */
8329         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8330           Store[x][y] = EL_EMPTY;
8331       }
8332
8333       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8334       {
8335         if (IS_MV_DIAGONAL(MovDir[x][y]))
8336         {
8337           int diagonal_move_dir = MovDir[x][y];
8338           int stored = Store[x][y];
8339           int change_delay = 8;
8340           int graphic;
8341
8342           /* android is moving diagonally */
8343
8344           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8345
8346           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8347           GfxElement[x][y] = EL_EMC_ANDROID;
8348           GfxAction[x][y] = ACTION_SHRINKING;
8349           GfxDir[x][y] = diagonal_move_dir;
8350           ChangeDelay[x][y] = change_delay;
8351
8352           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8353                                    GfxDir[x][y]);
8354
8355           DrawLevelGraphicAnimation(x, y, graphic);
8356           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8357
8358           if (Feld[newx][newy] == EL_ACID)
8359           {
8360             SplashAcid(newx, newy);
8361
8362             return;
8363           }
8364
8365           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8366
8367           Store[newx][newy] = EL_EMC_ANDROID;
8368           GfxElement[newx][newy] = EL_EMC_ANDROID;
8369           GfxAction[newx][newy] = ACTION_GROWING;
8370           GfxDir[newx][newy] = diagonal_move_dir;
8371           ChangeDelay[newx][newy] = change_delay;
8372
8373           graphic = el_act_dir2img(GfxElement[newx][newy],
8374                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8375
8376           DrawLevelGraphicAnimation(newx, newy, graphic);
8377           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8378
8379           return;
8380         }
8381         else
8382         {
8383           Feld[newx][newy] = EL_EMPTY;
8384           TEST_DrawLevelField(newx, newy);
8385
8386           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8387         }
8388       }
8389       else if (!IS_FREE(newx, newy))
8390       {
8391 #if 0
8392         if (IS_PLAYER(x, y))
8393           DrawPlayerField(x, y);
8394         else
8395           TEST_DrawLevelField(x, y);
8396 #endif
8397
8398         return;
8399       }
8400     }
8401     else if (IS_CUSTOM_ELEMENT(element) &&
8402              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8403     {
8404 #if 1
8405       if (!DigFieldByCE(newx, newy, element))
8406         return;
8407 #else
8408       int new_element = Feld[newx][newy];
8409
8410       if (!IS_FREE(newx, newy))
8411       {
8412         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8413                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8414                       ACTION_BREAKING);
8415
8416         /* no element can dig solid indestructible elements */
8417         if (IS_INDESTRUCTIBLE(new_element) &&
8418             !IS_DIGGABLE(new_element) &&
8419             !IS_COLLECTIBLE(new_element))
8420           return;
8421
8422         if (AmoebaNr[newx][newy] &&
8423             (new_element == EL_AMOEBA_FULL ||
8424              new_element == EL_BD_AMOEBA ||
8425              new_element == EL_AMOEBA_GROWING))
8426         {
8427           AmoebaCnt[AmoebaNr[newx][newy]]--;
8428           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8429         }
8430
8431         if (IS_MOVING(newx, newy))
8432           RemoveMovingField(newx, newy);
8433         else
8434         {
8435           RemoveField(newx, newy);
8436           TEST_DrawLevelField(newx, newy);
8437         }
8438
8439         /* if digged element was about to explode, prevent the explosion */
8440         ExplodeField[newx][newy] = EX_TYPE_NONE;
8441
8442         PlayLevelSoundAction(x, y, action);
8443       }
8444
8445       Store[newx][newy] = EL_EMPTY;
8446
8447 #if 1
8448       /* this makes it possible to leave the removed element again */
8449       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8450         Store[newx][newy] = new_element;
8451 #else
8452       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8453       {
8454         int move_leave_element = element_info[element].move_leave_element;
8455
8456         /* this makes it possible to leave the removed element again */
8457         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8458                              new_element : move_leave_element);
8459       }
8460 #endif
8461
8462 #endif
8463
8464       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8465       {
8466         RunnerVisit[x][y] = FrameCounter;
8467         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8468       }
8469     }
8470     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8471     {
8472       if (!IS_FREE(newx, newy))
8473       {
8474         if (IS_PLAYER(x, y))
8475           DrawPlayerField(x, y);
8476         else
8477           TEST_DrawLevelField(x, y);
8478
8479         return;
8480       }
8481       else
8482       {
8483         boolean wanna_flame = !RND(10);
8484         int dx = newx - x, dy = newy - y;
8485         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8486         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8487         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8488                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8489         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8490                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8491
8492         if ((wanna_flame ||
8493              IS_CLASSIC_ENEMY(element1) ||
8494              IS_CLASSIC_ENEMY(element2)) &&
8495             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8496             element1 != EL_FLAMES && element2 != EL_FLAMES)
8497         {
8498           ResetGfxAnimation(x, y);
8499           GfxAction[x][y] = ACTION_ATTACKING;
8500
8501           if (IS_PLAYER(x, y))
8502             DrawPlayerField(x, y);
8503           else
8504             TEST_DrawLevelField(x, y);
8505
8506           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8507
8508           MovDelay[x][y] = 50;
8509
8510           /* !!! */
8511 #if 0
8512           RemoveField(newx, newy);
8513 #endif
8514           Feld[newx][newy] = EL_FLAMES;
8515           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8516           {
8517 #if 0
8518             RemoveField(newx1, newy1);
8519 #endif
8520             Feld[newx1][newy1] = EL_FLAMES;
8521           }
8522           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8523           {
8524 #if 0
8525             RemoveField(newx2, newy2);
8526 #endif
8527             Feld[newx2][newy2] = EL_FLAMES;
8528           }
8529
8530           return;
8531         }
8532       }
8533     }
8534     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8535              Feld[newx][newy] == EL_DIAMOND)
8536     {
8537       if (IS_MOVING(newx, newy))
8538         RemoveMovingField(newx, newy);
8539       else
8540       {
8541         Feld[newx][newy] = EL_EMPTY;
8542         TEST_DrawLevelField(newx, newy);
8543       }
8544
8545       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8546     }
8547     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8548              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8549     {
8550       if (AmoebaNr[newx][newy])
8551       {
8552         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8553         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8554             Feld[newx][newy] == EL_BD_AMOEBA)
8555           AmoebaCnt[AmoebaNr[newx][newy]]--;
8556       }
8557
8558 #if 0
8559       /* !!! test !!! */
8560       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8561       {
8562         RemoveMovingField(newx, newy);
8563       }
8564 #else
8565       if (IS_MOVING(newx, newy))
8566       {
8567         RemoveMovingField(newx, newy);
8568       }
8569 #endif
8570       else
8571       {
8572         Feld[newx][newy] = EL_EMPTY;
8573         TEST_DrawLevelField(newx, newy);
8574       }
8575
8576       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8577     }
8578     else if ((element == EL_PACMAN || element == EL_MOLE)
8579              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8580     {
8581       if (AmoebaNr[newx][newy])
8582       {
8583         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8584         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8585             Feld[newx][newy] == EL_BD_AMOEBA)
8586           AmoebaCnt[AmoebaNr[newx][newy]]--;
8587       }
8588
8589       if (element == EL_MOLE)
8590       {
8591         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8592         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8593
8594         ResetGfxAnimation(x, y);
8595         GfxAction[x][y] = ACTION_DIGGING;
8596         TEST_DrawLevelField(x, y);
8597
8598         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8599
8600         return;                         /* wait for shrinking amoeba */
8601       }
8602       else      /* element == EL_PACMAN */
8603       {
8604         Feld[newx][newy] = EL_EMPTY;
8605         TEST_DrawLevelField(newx, newy);
8606         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8607       }
8608     }
8609     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8610              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8611               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8612     {
8613       /* wait for shrinking amoeba to completely disappear */
8614       return;
8615     }
8616     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8617     {
8618       /* object was running against a wall */
8619
8620       TurnRound(x, y);
8621
8622 #if 0
8623       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8624       if (move_pattern & MV_ANY_DIRECTION &&
8625           move_pattern == MovDir[x][y])
8626       {
8627         int blocking_element =
8628           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8629
8630         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8631                                  MovDir[x][y]);
8632
8633         element = Feld[x][y];   /* element might have changed */
8634       }
8635 #endif
8636
8637       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8638         DrawLevelElementAnimation(x, y, element);
8639
8640       if (DONT_TOUCH(element))
8641         TestIfBadThingTouchesPlayer(x, y);
8642
8643       return;
8644     }
8645
8646     InitMovingField(x, y, MovDir[x][y]);
8647
8648     PlayLevelSoundAction(x, y, ACTION_MOVING);
8649   }
8650
8651   if (MovDir[x][y])
8652     ContinueMoving(x, y);
8653 }
8654
8655 void ContinueMoving(int x, int y)
8656 {
8657   int element = Feld[x][y];
8658   struct ElementInfo *ei = &element_info[element];
8659   int direction = MovDir[x][y];
8660   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8661   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8662   int newx = x + dx, newy = y + dy;
8663   int stored = Store[x][y];
8664   int stored_new = Store[newx][newy];
8665   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8666   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8667   boolean last_line = (newy == lev_fieldy - 1);
8668
8669   MovPos[x][y] += getElementMoveStepsize(x, y);
8670
8671   if (pushed_by_player) /* special case: moving object pushed by player */
8672     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8673
8674   if (ABS(MovPos[x][y]) < TILEX)
8675   {
8676 #if 0
8677     int ee = Feld[x][y];
8678     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8679     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8680
8681     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8682            x, y, ABS(MovPos[x][y]),
8683            ee, gg, ff,
8684            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8685 #endif
8686
8687     TEST_DrawLevelField(x, y);
8688
8689     return;     /* element is still moving */
8690   }
8691
8692   /* element reached destination field */
8693
8694   Feld[x][y] = EL_EMPTY;
8695   Feld[newx][newy] = element;
8696   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8697
8698   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8699   {
8700     element = Feld[newx][newy] = EL_ACID;
8701   }
8702   else if (element == EL_MOLE)
8703   {
8704     Feld[x][y] = EL_SAND;
8705
8706     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8707   }
8708   else if (element == EL_QUICKSAND_FILLING)
8709   {
8710     element = Feld[newx][newy] = get_next_element(element);
8711     Store[newx][newy] = Store[x][y];
8712   }
8713   else if (element == EL_QUICKSAND_EMPTYING)
8714   {
8715     Feld[x][y] = get_next_element(element);
8716     element = Feld[newx][newy] = Store[x][y];
8717   }
8718   else if (element == EL_QUICKSAND_FAST_FILLING)
8719   {
8720     element = Feld[newx][newy] = get_next_element(element);
8721     Store[newx][newy] = Store[x][y];
8722   }
8723   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8724   {
8725     Feld[x][y] = get_next_element(element);
8726     element = Feld[newx][newy] = Store[x][y];
8727   }
8728   else if (element == EL_MAGIC_WALL_FILLING)
8729   {
8730     element = Feld[newx][newy] = get_next_element(element);
8731     if (!game.magic_wall_active)
8732       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8733     Store[newx][newy] = Store[x][y];
8734   }
8735   else if (element == EL_MAGIC_WALL_EMPTYING)
8736   {
8737     Feld[x][y] = get_next_element(element);
8738     if (!game.magic_wall_active)
8739       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8740     element = Feld[newx][newy] = Store[x][y];
8741
8742 #if USE_NEW_CUSTOM_VALUE
8743     InitField(newx, newy, FALSE);
8744 #endif
8745   }
8746   else if (element == EL_BD_MAGIC_WALL_FILLING)
8747   {
8748     element = Feld[newx][newy] = get_next_element(element);
8749     if (!game.magic_wall_active)
8750       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8751     Store[newx][newy] = Store[x][y];
8752   }
8753   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8754   {
8755     Feld[x][y] = get_next_element(element);
8756     if (!game.magic_wall_active)
8757       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8758     element = Feld[newx][newy] = Store[x][y];
8759
8760 #if USE_NEW_CUSTOM_VALUE
8761     InitField(newx, newy, FALSE);
8762 #endif
8763   }
8764   else if (element == EL_DC_MAGIC_WALL_FILLING)
8765   {
8766     element = Feld[newx][newy] = get_next_element(element);
8767     if (!game.magic_wall_active)
8768       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8769     Store[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8772   {
8773     Feld[x][y] = get_next_element(element);
8774     if (!game.magic_wall_active)
8775       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8776     element = Feld[newx][newy] = Store[x][y];
8777
8778 #if USE_NEW_CUSTOM_VALUE
8779     InitField(newx, newy, FALSE);
8780 #endif
8781   }
8782   else if (element == EL_AMOEBA_DROPPING)
8783   {
8784     Feld[x][y] = get_next_element(element);
8785     element = Feld[newx][newy] = Store[x][y];
8786   }
8787   else if (element == EL_SOKOBAN_OBJECT)
8788   {
8789     if (Back[x][y])
8790       Feld[x][y] = Back[x][y];
8791
8792     if (Back[newx][newy])
8793       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8794
8795     Back[x][y] = Back[newx][newy] = 0;
8796   }
8797
8798   Store[x][y] = EL_EMPTY;
8799   MovPos[x][y] = 0;
8800   MovDir[x][y] = 0;
8801   MovDelay[x][y] = 0;
8802
8803   MovDelay[newx][newy] = 0;
8804
8805   if (CAN_CHANGE_OR_HAS_ACTION(element))
8806   {
8807     /* copy element change control values to new field */
8808     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8809     ChangePage[newx][newy]  = ChangePage[x][y];
8810     ChangeCount[newx][newy] = ChangeCount[x][y];
8811     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8812   }
8813
8814 #if USE_NEW_CUSTOM_VALUE
8815   CustomValue[newx][newy] = CustomValue[x][y];
8816 #endif
8817
8818   ChangeDelay[x][y] = 0;
8819   ChangePage[x][y] = -1;
8820   ChangeCount[x][y] = 0;
8821   ChangeEvent[x][y] = -1;
8822
8823 #if USE_NEW_CUSTOM_VALUE
8824   CustomValue[x][y] = 0;
8825 #endif
8826
8827   /* copy animation control values to new field */
8828   GfxFrame[newx][newy]  = GfxFrame[x][y];
8829   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8830   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8831   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8832
8833   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8834
8835   /* some elements can leave other elements behind after moving */
8836 #if 1
8837   if (ei->move_leave_element != EL_EMPTY &&
8838       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8839       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8840 #else
8841   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8842       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8843       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8844 #endif
8845   {
8846     int move_leave_element = ei->move_leave_element;
8847
8848 #if 1
8849 #if 1
8850     /* this makes it possible to leave the removed element again */
8851     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8852       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8853 #else
8854     /* this makes it possible to leave the removed element again */
8855     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8856       move_leave_element = stored;
8857 #endif
8858 #else
8859     /* this makes it possible to leave the removed element again */
8860     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8861         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8862       move_leave_element = stored;
8863 #endif
8864
8865     Feld[x][y] = move_leave_element;
8866
8867     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8868       MovDir[x][y] = direction;
8869
8870     InitField(x, y, FALSE);
8871
8872     if (GFX_CRUMBLED(Feld[x][y]))
8873       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8874
8875     if (ELEM_IS_PLAYER(move_leave_element))
8876       RelocatePlayer(x, y, move_leave_element);
8877   }
8878
8879   /* do this after checking for left-behind element */
8880   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8881
8882   if (!CAN_MOVE(element) ||
8883       (CAN_FALL(element) && direction == MV_DOWN &&
8884        (element == EL_SPRING ||
8885         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8886         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8887     GfxDir[x][y] = MovDir[newx][newy] = 0;
8888
8889   TEST_DrawLevelField(x, y);
8890   TEST_DrawLevelField(newx, newy);
8891
8892   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8893
8894   /* prevent pushed element from moving on in pushed direction */
8895   if (pushed_by_player && CAN_MOVE(element) &&
8896       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8897       !(element_info[element].move_pattern & direction))
8898     TurnRound(newx, newy);
8899
8900   /* prevent elements on conveyor belt from moving on in last direction */
8901   if (pushed_by_conveyor && CAN_FALL(element) &&
8902       direction & MV_HORIZONTAL)
8903     MovDir[newx][newy] = 0;
8904
8905   if (!pushed_by_player)
8906   {
8907     int nextx = newx + dx, nexty = newy + dy;
8908     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8909
8910     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8911
8912     if (CAN_FALL(element) && direction == MV_DOWN)
8913       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8914
8915     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8916       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8917
8918 #if USE_FIX_IMPACT_COLLISION
8919     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8920       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8921 #endif
8922   }
8923
8924   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8925   {
8926     TestIfBadThingTouchesPlayer(newx, newy);
8927     TestIfBadThingTouchesFriend(newx, newy);
8928
8929     if (!IS_CUSTOM_ELEMENT(element))
8930       TestIfBadThingTouchesOtherBadThing(newx, newy);
8931   }
8932   else if (element == EL_PENGUIN)
8933     TestIfFriendTouchesBadThing(newx, newy);
8934
8935   /* give the player one last chance (one more frame) to move away */
8936   if (CAN_FALL(element) && direction == MV_DOWN &&
8937       (last_line || (!IS_FREE(x, newy + 1) &&
8938                      (!IS_PLAYER(x, newy + 1) ||
8939                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8940     Impact(x, newy);
8941
8942   if (pushed_by_player && !game.use_change_when_pushing_bug)
8943   {
8944     int push_side = MV_DIR_OPPOSITE(direction);
8945     struct PlayerInfo *player = PLAYERINFO(x, y);
8946
8947     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8948                                player->index_bit, push_side);
8949     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8950                                         player->index_bit, push_side);
8951   }
8952
8953   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8954     MovDelay[newx][newy] = 1;
8955
8956   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8957
8958   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8959
8960 #if 0
8961   if (ChangePage[newx][newy] != -1)             /* delayed change */
8962   {
8963     int page = ChangePage[newx][newy];
8964     struct ElementChangeInfo *change = &ei->change_page[page];
8965
8966     ChangePage[newx][newy] = -1;
8967
8968     if (change->can_change)
8969     {
8970       if (ChangeElement(newx, newy, element, page))
8971       {
8972         if (change->post_change_function)
8973           change->post_change_function(newx, newy);
8974       }
8975     }
8976
8977     if (change->has_action)
8978       ExecuteCustomElementAction(newx, newy, element, page);
8979   }
8980 #endif
8981
8982   TestIfElementHitsCustomElement(newx, newy, direction);
8983   TestIfPlayerTouchesCustomElement(newx, newy);
8984   TestIfElementTouchesCustomElement(newx, newy);
8985
8986   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8987       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8988     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8989                              MV_DIR_OPPOSITE(direction));
8990 }
8991
8992 int AmoebeNachbarNr(int ax, int ay)
8993 {
8994   int i;
8995   int element = Feld[ax][ay];
8996   int group_nr = 0;
8997   static int xy[4][2] =
8998   {
8999     { 0, -1 },
9000     { -1, 0 },
9001     { +1, 0 },
9002     { 0, +1 }
9003   };
9004
9005   for (i = 0; i < NUM_DIRECTIONS; i++)
9006   {
9007     int x = ax + xy[i][0];
9008     int y = ay + xy[i][1];
9009
9010     if (!IN_LEV_FIELD(x, y))
9011       continue;
9012
9013     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9014       group_nr = AmoebaNr[x][y];
9015   }
9016
9017   return group_nr;
9018 }
9019
9020 void AmoebenVereinigen(int ax, int ay)
9021 {
9022   int i, x, y, xx, yy;
9023   int new_group_nr = AmoebaNr[ax][ay];
9024   static int xy[4][2] =
9025   {
9026     { 0, -1 },
9027     { -1, 0 },
9028     { +1, 0 },
9029     { 0, +1 }
9030   };
9031
9032   if (new_group_nr == 0)
9033     return;
9034
9035   for (i = 0; i < NUM_DIRECTIONS; i++)
9036   {
9037     x = ax + xy[i][0];
9038     y = ay + xy[i][1];
9039
9040     if (!IN_LEV_FIELD(x, y))
9041       continue;
9042
9043     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9044          Feld[x][y] == EL_BD_AMOEBA ||
9045          Feld[x][y] == EL_AMOEBA_DEAD) &&
9046         AmoebaNr[x][y] != new_group_nr)
9047     {
9048       int old_group_nr = AmoebaNr[x][y];
9049
9050       if (old_group_nr == 0)
9051         return;
9052
9053       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9054       AmoebaCnt[old_group_nr] = 0;
9055       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9056       AmoebaCnt2[old_group_nr] = 0;
9057
9058       SCAN_PLAYFIELD(xx, yy)
9059       {
9060         if (AmoebaNr[xx][yy] == old_group_nr)
9061           AmoebaNr[xx][yy] = new_group_nr;
9062       }
9063     }
9064   }
9065 }
9066
9067 void AmoebeUmwandeln(int ax, int ay)
9068 {
9069   int i, x, y;
9070
9071   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9072   {
9073     int group_nr = AmoebaNr[ax][ay];
9074
9075 #ifdef DEBUG
9076     if (group_nr == 0)
9077     {
9078       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9079       printf("AmoebeUmwandeln(): This should never happen!\n");
9080       return;
9081     }
9082 #endif
9083
9084     SCAN_PLAYFIELD(x, y)
9085     {
9086       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9087       {
9088         AmoebaNr[x][y] = 0;
9089         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9090       }
9091     }
9092
9093     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9094                             SND_AMOEBA_TURNING_TO_GEM :
9095                             SND_AMOEBA_TURNING_TO_ROCK));
9096     Bang(ax, ay);
9097   }
9098   else
9099   {
9100     static int xy[4][2] =
9101     {
9102       { 0, -1 },
9103       { -1, 0 },
9104       { +1, 0 },
9105       { 0, +1 }
9106     };
9107
9108     for (i = 0; i < NUM_DIRECTIONS; i++)
9109     {
9110       x = ax + xy[i][0];
9111       y = ay + xy[i][1];
9112
9113       if (!IN_LEV_FIELD(x, y))
9114         continue;
9115
9116       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9117       {
9118         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9119                               SND_AMOEBA_TURNING_TO_GEM :
9120                               SND_AMOEBA_TURNING_TO_ROCK));
9121         Bang(x, y);
9122       }
9123     }
9124   }
9125 }
9126
9127 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9128 {
9129   int x, y;
9130   int group_nr = AmoebaNr[ax][ay];
9131   boolean done = FALSE;
9132
9133 #ifdef DEBUG
9134   if (group_nr == 0)
9135   {
9136     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9137     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9138     return;
9139   }
9140 #endif
9141
9142   SCAN_PLAYFIELD(x, y)
9143   {
9144     if (AmoebaNr[x][y] == group_nr &&
9145         (Feld[x][y] == EL_AMOEBA_DEAD ||
9146          Feld[x][y] == EL_BD_AMOEBA ||
9147          Feld[x][y] == EL_AMOEBA_GROWING))
9148     {
9149       AmoebaNr[x][y] = 0;
9150       Feld[x][y] = new_element;
9151       InitField(x, y, FALSE);
9152       TEST_DrawLevelField(x, y);
9153       done = TRUE;
9154     }
9155   }
9156
9157   if (done)
9158     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9159                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9160                             SND_BD_AMOEBA_TURNING_TO_GEM));
9161 }
9162
9163 void AmoebeWaechst(int x, int y)
9164 {
9165   static unsigned long sound_delay = 0;
9166   static unsigned long sound_delay_value = 0;
9167
9168   if (!MovDelay[x][y])          /* start new growing cycle */
9169   {
9170     MovDelay[x][y] = 7;
9171
9172     if (DelayReached(&sound_delay, sound_delay_value))
9173     {
9174       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9175       sound_delay_value = 30;
9176     }
9177   }
9178
9179   if (MovDelay[x][y])           /* wait some time before growing bigger */
9180   {
9181     MovDelay[x][y]--;
9182     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9183     {
9184       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9185                                            6 - MovDelay[x][y]);
9186
9187       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9188     }
9189
9190     if (!MovDelay[x][y])
9191     {
9192       Feld[x][y] = Store[x][y];
9193       Store[x][y] = 0;
9194       TEST_DrawLevelField(x, y);
9195     }
9196   }
9197 }
9198
9199 void AmoebaDisappearing(int x, int y)
9200 {
9201   static unsigned long sound_delay = 0;
9202   static unsigned long sound_delay_value = 0;
9203
9204   if (!MovDelay[x][y])          /* start new shrinking cycle */
9205   {
9206     MovDelay[x][y] = 7;
9207
9208     if (DelayReached(&sound_delay, sound_delay_value))
9209       sound_delay_value = 30;
9210   }
9211
9212   if (MovDelay[x][y])           /* wait some time before shrinking */
9213   {
9214     MovDelay[x][y]--;
9215     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9216     {
9217       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9218                                            6 - MovDelay[x][y]);
9219
9220       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9221     }
9222
9223     if (!MovDelay[x][y])
9224     {
9225       Feld[x][y] = EL_EMPTY;
9226       TEST_DrawLevelField(x, y);
9227
9228       /* don't let mole enter this field in this cycle;
9229          (give priority to objects falling to this field from above) */
9230       Stop[x][y] = TRUE;
9231     }
9232   }
9233 }
9234
9235 void AmoebeAbleger(int ax, int ay)
9236 {
9237   int i;
9238   int element = Feld[ax][ay];
9239   int graphic = el2img(element);
9240   int newax = ax, neway = ay;
9241   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9242   static int xy[4][2] =
9243   {
9244     { 0, -1 },
9245     { -1, 0 },
9246     { +1, 0 },
9247     { 0, +1 }
9248   };
9249
9250   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9251   {
9252     Feld[ax][ay] = EL_AMOEBA_DEAD;
9253     TEST_DrawLevelField(ax, ay);
9254     return;
9255   }
9256
9257   if (IS_ANIMATED(graphic))
9258     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9259
9260   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9261     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9262
9263   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9264   {
9265     MovDelay[ax][ay]--;
9266     if (MovDelay[ax][ay])
9267       return;
9268   }
9269
9270   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9271   {
9272     int start = RND(4);
9273     int x = ax + xy[start][0];
9274     int y = ay + xy[start][1];
9275
9276     if (!IN_LEV_FIELD(x, y))
9277       return;
9278
9279     if (IS_FREE(x, y) ||
9280         CAN_GROW_INTO(Feld[x][y]) ||
9281         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9282         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9283     {
9284       newax = x;
9285       neway = y;
9286     }
9287
9288     if (newax == ax && neway == ay)
9289       return;
9290   }
9291   else                          /* normal or "filled" (BD style) amoeba */
9292   {
9293     int start = RND(4);
9294     boolean waiting_for_player = FALSE;
9295
9296     for (i = 0; i < NUM_DIRECTIONS; i++)
9297     {
9298       int j = (start + i) % 4;
9299       int x = ax + xy[j][0];
9300       int y = ay + xy[j][1];
9301
9302       if (!IN_LEV_FIELD(x, y))
9303         continue;
9304
9305       if (IS_FREE(x, y) ||
9306           CAN_GROW_INTO(Feld[x][y]) ||
9307           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9308           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9309       {
9310         newax = x;
9311         neway = y;
9312         break;
9313       }
9314       else if (IS_PLAYER(x, y))
9315         waiting_for_player = TRUE;
9316     }
9317
9318     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9319     {
9320       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9321       {
9322         Feld[ax][ay] = EL_AMOEBA_DEAD;
9323         TEST_DrawLevelField(ax, ay);
9324         AmoebaCnt[AmoebaNr[ax][ay]]--;
9325
9326         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9327         {
9328           if (element == EL_AMOEBA_FULL)
9329             AmoebeUmwandeln(ax, ay);
9330           else if (element == EL_BD_AMOEBA)
9331             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9332         }
9333       }
9334       return;
9335     }
9336     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9337     {
9338       /* amoeba gets larger by growing in some direction */
9339
9340       int new_group_nr = AmoebaNr[ax][ay];
9341
9342 #ifdef DEBUG
9343   if (new_group_nr == 0)
9344   {
9345     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9346     printf("AmoebeAbleger(): This should never happen!\n");
9347     return;
9348   }
9349 #endif
9350
9351       AmoebaNr[newax][neway] = new_group_nr;
9352       AmoebaCnt[new_group_nr]++;
9353       AmoebaCnt2[new_group_nr]++;
9354
9355       /* if amoeba touches other amoeba(s) after growing, unify them */
9356       AmoebenVereinigen(newax, neway);
9357
9358       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9359       {
9360         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9361         return;
9362       }
9363     }
9364   }
9365
9366   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9367       (neway == lev_fieldy - 1 && newax != ax))
9368   {
9369     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9370     Store[newax][neway] = element;
9371   }
9372   else if (neway == ay || element == EL_EMC_DRIPPER)
9373   {
9374     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9375
9376     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9377   }
9378   else
9379   {
9380     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9381     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9382     Store[ax][ay] = EL_AMOEBA_DROP;
9383     ContinueMoving(ax, ay);
9384     return;
9385   }
9386
9387   TEST_DrawLevelField(newax, neway);
9388 }
9389
9390 void Life(int ax, int ay)
9391 {
9392   int x1, y1, x2, y2;
9393   int life_time = 40;
9394   int element = Feld[ax][ay];
9395   int graphic = el2img(element);
9396   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9397                          level.biomaze);
9398   boolean changed = FALSE;
9399
9400   if (IS_ANIMATED(graphic))
9401     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9402
9403   if (Stop[ax][ay])
9404     return;
9405
9406   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9407     MovDelay[ax][ay] = life_time;
9408
9409   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9410   {
9411     MovDelay[ax][ay]--;
9412     if (MovDelay[ax][ay])
9413       return;
9414   }
9415
9416   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9417   {
9418     int xx = ax+x1, yy = ay+y1;
9419     int nachbarn = 0;
9420
9421     if (!IN_LEV_FIELD(xx, yy))
9422       continue;
9423
9424     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9425     {
9426       int x = xx+x2, y = yy+y2;
9427
9428       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9429         continue;
9430
9431       if (((Feld[x][y] == element ||
9432             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9433            !Stop[x][y]) ||
9434           (IS_FREE(x, y) && Stop[x][y]))
9435         nachbarn++;
9436     }
9437
9438     if (xx == ax && yy == ay)           /* field in the middle */
9439     {
9440       if (nachbarn < life_parameter[0] ||
9441           nachbarn > life_parameter[1])
9442       {
9443         Feld[xx][yy] = EL_EMPTY;
9444         if (!Stop[xx][yy])
9445           TEST_DrawLevelField(xx, yy);
9446         Stop[xx][yy] = TRUE;
9447         changed = TRUE;
9448       }
9449     }
9450     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9451     {                                   /* free border field */
9452       if (nachbarn >= life_parameter[2] &&
9453           nachbarn <= life_parameter[3])
9454       {
9455         Feld[xx][yy] = element;
9456         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9457         if (!Stop[xx][yy])
9458           TEST_DrawLevelField(xx, yy);
9459         Stop[xx][yy] = TRUE;
9460         changed = TRUE;
9461       }
9462     }
9463   }
9464
9465   if (changed)
9466     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9467                    SND_GAME_OF_LIFE_GROWING);
9468 }
9469
9470 static void InitRobotWheel(int x, int y)
9471 {
9472   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9473 }
9474
9475 static void RunRobotWheel(int x, int y)
9476 {
9477   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9478 }
9479
9480 static void StopRobotWheel(int x, int y)
9481 {
9482   if (ZX == x && ZY == y)
9483   {
9484     ZX = ZY = -1;
9485
9486     game.robot_wheel_active = FALSE;
9487   }
9488 }
9489
9490 static void InitTimegateWheel(int x, int y)
9491 {
9492   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9493 }
9494
9495 static void RunTimegateWheel(int x, int y)
9496 {
9497   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9498 }
9499
9500 static void InitMagicBallDelay(int x, int y)
9501 {
9502 #if 1
9503   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9504 #else
9505   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9506 #endif
9507 }
9508
9509 static void ActivateMagicBall(int bx, int by)
9510 {
9511   int x, y;
9512
9513   if (level.ball_random)
9514   {
9515     int pos_border = RND(8);    /* select one of the eight border elements */
9516     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9517     int xx = pos_content % 3;
9518     int yy = pos_content / 3;
9519
9520     x = bx - 1 + xx;
9521     y = by - 1 + yy;
9522
9523     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9524       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9525   }
9526   else
9527   {
9528     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9529     {
9530       int xx = x - bx + 1;
9531       int yy = y - by + 1;
9532
9533       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9534         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9535     }
9536   }
9537
9538   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9539 }
9540
9541 void CheckExit(int x, int y)
9542 {
9543   if (local_player->gems_still_needed > 0 ||
9544       local_player->sokobanfields_still_needed > 0 ||
9545       local_player->lights_still_needed > 0)
9546   {
9547     int element = Feld[x][y];
9548     int graphic = el2img(element);
9549
9550     if (IS_ANIMATED(graphic))
9551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9552
9553     return;
9554   }
9555
9556   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9557     return;
9558
9559   Feld[x][y] = EL_EXIT_OPENING;
9560
9561   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9562 }
9563
9564 void CheckExitEM(int x, int y)
9565 {
9566   if (local_player->gems_still_needed > 0 ||
9567       local_player->sokobanfields_still_needed > 0 ||
9568       local_player->lights_still_needed > 0)
9569   {
9570     int element = Feld[x][y];
9571     int graphic = el2img(element);
9572
9573     if (IS_ANIMATED(graphic))
9574       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9575
9576     return;
9577   }
9578
9579   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9580     return;
9581
9582   Feld[x][y] = EL_EM_EXIT_OPENING;
9583
9584   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9585 }
9586
9587 void CheckExitSteel(int x, int y)
9588 {
9589   if (local_player->gems_still_needed > 0 ||
9590       local_player->sokobanfields_still_needed > 0 ||
9591       local_player->lights_still_needed > 0)
9592   {
9593     int element = Feld[x][y];
9594     int graphic = el2img(element);
9595
9596     if (IS_ANIMATED(graphic))
9597       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9598
9599     return;
9600   }
9601
9602   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9603     return;
9604
9605   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9606
9607   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9608 }
9609
9610 void CheckExitSteelEM(int x, int y)
9611 {
9612   if (local_player->gems_still_needed > 0 ||
9613       local_player->sokobanfields_still_needed > 0 ||
9614       local_player->lights_still_needed > 0)
9615   {
9616     int element = Feld[x][y];
9617     int graphic = el2img(element);
9618
9619     if (IS_ANIMATED(graphic))
9620       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9621
9622     return;
9623   }
9624
9625   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9626     return;
9627
9628   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9629
9630   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9631 }
9632
9633 void CheckExitSP(int x, int y)
9634 {
9635   if (local_player->gems_still_needed > 0)
9636   {
9637     int element = Feld[x][y];
9638     int graphic = el2img(element);
9639
9640     if (IS_ANIMATED(graphic))
9641       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9642
9643     return;
9644   }
9645
9646   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9647     return;
9648
9649   Feld[x][y] = EL_SP_EXIT_OPENING;
9650
9651   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9652 }
9653
9654 static void CloseAllOpenTimegates()
9655 {
9656   int x, y;
9657
9658   SCAN_PLAYFIELD(x, y)
9659   {
9660     int element = Feld[x][y];
9661
9662     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9663     {
9664       Feld[x][y] = EL_TIMEGATE_CLOSING;
9665
9666       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9667     }
9668   }
9669 }
9670
9671 void DrawTwinkleOnField(int x, int y)
9672 {
9673   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9674     return;
9675
9676   if (Feld[x][y] == EL_BD_DIAMOND)
9677     return;
9678
9679   if (MovDelay[x][y] == 0)      /* next animation frame */
9680     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9681
9682   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9683   {
9684     MovDelay[x][y]--;
9685
9686     DrawLevelElementAnimation(x, y, Feld[x][y]);
9687
9688     if (MovDelay[x][y] != 0)
9689     {
9690       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9691                                            10 - MovDelay[x][y]);
9692
9693       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9694     }
9695   }
9696 }
9697
9698 void MauerWaechst(int x, int y)
9699 {
9700   int delay = 6;
9701
9702   if (!MovDelay[x][y])          /* next animation frame */
9703     MovDelay[x][y] = 3 * delay;
9704
9705   if (MovDelay[x][y])           /* wait some time before next frame */
9706   {
9707     MovDelay[x][y]--;
9708
9709     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9710     {
9711       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9712       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9713
9714       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9715     }
9716
9717     if (!MovDelay[x][y])
9718     {
9719       if (MovDir[x][y] == MV_LEFT)
9720       {
9721         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9722           TEST_DrawLevelField(x - 1, y);
9723       }
9724       else if (MovDir[x][y] == MV_RIGHT)
9725       {
9726         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9727           TEST_DrawLevelField(x + 1, y);
9728       }
9729       else if (MovDir[x][y] == MV_UP)
9730       {
9731         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9732           TEST_DrawLevelField(x, y - 1);
9733       }
9734       else
9735       {
9736         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9737           TEST_DrawLevelField(x, y + 1);
9738       }
9739
9740       Feld[x][y] = Store[x][y];
9741       Store[x][y] = 0;
9742       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9743       TEST_DrawLevelField(x, y);
9744     }
9745   }
9746 }
9747
9748 void MauerAbleger(int ax, int ay)
9749 {
9750   int element = Feld[ax][ay];
9751   int graphic = el2img(element);
9752   boolean oben_frei = FALSE, unten_frei = FALSE;
9753   boolean links_frei = FALSE, rechts_frei = FALSE;
9754   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9755   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9756   boolean new_wall = FALSE;
9757
9758   if (IS_ANIMATED(graphic))
9759     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9760
9761   if (!MovDelay[ax][ay])        /* start building new wall */
9762     MovDelay[ax][ay] = 6;
9763
9764   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9765   {
9766     MovDelay[ax][ay]--;
9767     if (MovDelay[ax][ay])
9768       return;
9769   }
9770
9771   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9772     oben_frei = TRUE;
9773   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9774     unten_frei = TRUE;
9775   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9776     links_frei = TRUE;
9777   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9778     rechts_frei = TRUE;
9779
9780   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9781       element == EL_EXPANDABLE_WALL_ANY)
9782   {
9783     if (oben_frei)
9784     {
9785       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9786       Store[ax][ay-1] = element;
9787       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9788       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9789         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9790                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9791       new_wall = TRUE;
9792     }
9793     if (unten_frei)
9794     {
9795       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9796       Store[ax][ay+1] = element;
9797       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9798       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9799         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9800                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9801       new_wall = TRUE;
9802     }
9803   }
9804
9805   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9806       element == EL_EXPANDABLE_WALL_ANY ||
9807       element == EL_EXPANDABLE_WALL ||
9808       element == EL_BD_EXPANDABLE_WALL)
9809   {
9810     if (links_frei)
9811     {
9812       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9813       Store[ax-1][ay] = element;
9814       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9815       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9816         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9817                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9818       new_wall = TRUE;
9819     }
9820
9821     if (rechts_frei)
9822     {
9823       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9824       Store[ax+1][ay] = element;
9825       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9826       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9827         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9828                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9829       new_wall = TRUE;
9830     }
9831   }
9832
9833   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9834     TEST_DrawLevelField(ax, ay);
9835
9836   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9837     oben_massiv = TRUE;
9838   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9839     unten_massiv = TRUE;
9840   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9841     links_massiv = TRUE;
9842   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9843     rechts_massiv = TRUE;
9844
9845   if (((oben_massiv && unten_massiv) ||
9846        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9847        element == EL_EXPANDABLE_WALL) &&
9848       ((links_massiv && rechts_massiv) ||
9849        element == EL_EXPANDABLE_WALL_VERTICAL))
9850     Feld[ax][ay] = EL_WALL;
9851
9852   if (new_wall)
9853     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9854 }
9855
9856 void MauerAblegerStahl(int ax, int ay)
9857 {
9858   int element = Feld[ax][ay];
9859   int graphic = el2img(element);
9860   boolean oben_frei = FALSE, unten_frei = FALSE;
9861   boolean links_frei = FALSE, rechts_frei = FALSE;
9862   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9863   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9864   boolean new_wall = FALSE;
9865
9866   if (IS_ANIMATED(graphic))
9867     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9868
9869   if (!MovDelay[ax][ay])        /* start building new wall */
9870     MovDelay[ax][ay] = 6;
9871
9872   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9873   {
9874     MovDelay[ax][ay]--;
9875     if (MovDelay[ax][ay])
9876       return;
9877   }
9878
9879   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9880     oben_frei = TRUE;
9881   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9882     unten_frei = TRUE;
9883   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9884     links_frei = TRUE;
9885   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9886     rechts_frei = TRUE;
9887
9888   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9889       element == EL_EXPANDABLE_STEELWALL_ANY)
9890   {
9891     if (oben_frei)
9892     {
9893       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9894       Store[ax][ay-1] = element;
9895       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9896       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9897         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9898                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9899       new_wall = TRUE;
9900     }
9901     if (unten_frei)
9902     {
9903       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9904       Store[ax][ay+1] = element;
9905       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9906       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9907         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9908                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9909       new_wall = TRUE;
9910     }
9911   }
9912
9913   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9914       element == EL_EXPANDABLE_STEELWALL_ANY)
9915   {
9916     if (links_frei)
9917     {
9918       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9919       Store[ax-1][ay] = element;
9920       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9921       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9922         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9923                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9924       new_wall = TRUE;
9925     }
9926
9927     if (rechts_frei)
9928     {
9929       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9930       Store[ax+1][ay] = element;
9931       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9932       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9933         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9934                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9935       new_wall = TRUE;
9936     }
9937   }
9938
9939   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9940     oben_massiv = TRUE;
9941   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9942     unten_massiv = TRUE;
9943   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9944     links_massiv = TRUE;
9945   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9946     rechts_massiv = TRUE;
9947
9948   if (((oben_massiv && unten_massiv) ||
9949        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9950       ((links_massiv && rechts_massiv) ||
9951        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9952     Feld[ax][ay] = EL_STEELWALL;
9953
9954   if (new_wall)
9955     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9956 }
9957
9958 void CheckForDragon(int x, int y)
9959 {
9960   int i, j;
9961   boolean dragon_found = FALSE;
9962   static int xy[4][2] =
9963   {
9964     { 0, -1 },
9965     { -1, 0 },
9966     { +1, 0 },
9967     { 0, +1 }
9968   };
9969
9970   for (i = 0; i < NUM_DIRECTIONS; i++)
9971   {
9972     for (j = 0; j < 4; j++)
9973     {
9974       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9975
9976       if (IN_LEV_FIELD(xx, yy) &&
9977           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9978       {
9979         if (Feld[xx][yy] == EL_DRAGON)
9980           dragon_found = TRUE;
9981       }
9982       else
9983         break;
9984     }
9985   }
9986
9987   if (!dragon_found)
9988   {
9989     for (i = 0; i < NUM_DIRECTIONS; i++)
9990     {
9991       for (j = 0; j < 3; j++)
9992       {
9993         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9994   
9995         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9996         {
9997           Feld[xx][yy] = EL_EMPTY;
9998           TEST_DrawLevelField(xx, yy);
9999         }
10000         else
10001           break;
10002       }
10003     }
10004   }
10005 }
10006
10007 static void InitBuggyBase(int x, int y)
10008 {
10009   int element = Feld[x][y];
10010   int activating_delay = FRAMES_PER_SECOND / 4;
10011
10012   ChangeDelay[x][y] =
10013     (element == EL_SP_BUGGY_BASE ?
10014      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10015      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10016      activating_delay :
10017      element == EL_SP_BUGGY_BASE_ACTIVE ?
10018      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10019 }
10020
10021 static void WarnBuggyBase(int x, int y)
10022 {
10023   int i;
10024   static int xy[4][2] =
10025   {
10026     { 0, -1 },
10027     { -1, 0 },
10028     { +1, 0 },
10029     { 0, +1 }
10030   };
10031
10032   for (i = 0; i < NUM_DIRECTIONS; i++)
10033   {
10034     int xx = x + xy[i][0];
10035     int yy = y + xy[i][1];
10036
10037     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10038     {
10039       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10040
10041       break;
10042     }
10043   }
10044 }
10045
10046 static void InitTrap(int x, int y)
10047 {
10048   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10049 }
10050
10051 static void ActivateTrap(int x, int y)
10052 {
10053   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10054 }
10055
10056 static void ChangeActiveTrap(int x, int y)
10057 {
10058   int graphic = IMG_TRAP_ACTIVE;
10059
10060   /* if new animation frame was drawn, correct crumbled sand border */
10061   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10062     TEST_DrawLevelFieldCrumbledSand(x, y);
10063 }
10064
10065 static int getSpecialActionElement(int element, int number, int base_element)
10066 {
10067   return (element != EL_EMPTY ? element :
10068           number != -1 ? base_element + number - 1 :
10069           EL_EMPTY);
10070 }
10071
10072 static int getModifiedActionNumber(int value_old, int operator, int operand,
10073                                    int value_min, int value_max)
10074 {
10075   int value_new = (operator == CA_MODE_SET      ? operand :
10076                    operator == CA_MODE_ADD      ? value_old + operand :
10077                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10078                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10079                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10080                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10081                    value_old);
10082
10083   return (value_new < value_min ? value_min :
10084           value_new > value_max ? value_max :
10085           value_new);
10086 }
10087
10088 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10089 {
10090   struct ElementInfo *ei = &element_info[element];
10091   struct ElementChangeInfo *change = &ei->change_page[page];
10092   int target_element = change->target_element;
10093   int action_type = change->action_type;
10094   int action_mode = change->action_mode;
10095   int action_arg = change->action_arg;
10096   int i;
10097
10098   if (!change->has_action)
10099     return;
10100
10101   /* ---------- determine action paramater values -------------------------- */
10102
10103   int level_time_value =
10104     (level.time > 0 ? TimeLeft :
10105      TimePlayed);
10106
10107   int action_arg_element =
10108     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10109      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10110      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10111      EL_EMPTY);
10112
10113   int action_arg_direction =
10114     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10115      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10116      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10117      change->actual_trigger_side :
10118      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10119      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10120      MV_NONE);
10121
10122   int action_arg_number_min =
10123     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10124      CA_ARG_MIN);
10125
10126   int action_arg_number_max =
10127     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10128      action_type == CA_SET_LEVEL_GEMS ? 999 :
10129      action_type == CA_SET_LEVEL_TIME ? 9999 :
10130      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10131      action_type == CA_SET_CE_VALUE ? 9999 :
10132      action_type == CA_SET_CE_SCORE ? 9999 :
10133      CA_ARG_MAX);
10134
10135   int action_arg_number_reset =
10136     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10137      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10138      action_type == CA_SET_LEVEL_TIME ? level.time :
10139      action_type == CA_SET_LEVEL_SCORE ? 0 :
10140 #if USE_NEW_CUSTOM_VALUE
10141      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10142 #else
10143      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10144 #endif
10145      action_type == CA_SET_CE_SCORE ? 0 :
10146      0);
10147
10148   int action_arg_number =
10149     (action_arg <= CA_ARG_MAX ? action_arg :
10150      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10151      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10152      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10153      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10154      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10155      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10156 #if USE_NEW_CUSTOM_VALUE
10157      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10158 #else
10159      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10160 #endif
10161      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10162      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10163      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10164      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10165      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10166      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10167      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10168      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10169      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10170      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10171      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10172      -1);
10173
10174   int action_arg_number_old =
10175     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10176      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10177      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10178      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10179      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10180      0);
10181
10182   int action_arg_number_new =
10183     getModifiedActionNumber(action_arg_number_old,
10184                             action_mode, action_arg_number,
10185                             action_arg_number_min, action_arg_number_max);
10186
10187 #if 1
10188   int trigger_player_bits = change->actual_trigger_player_bits;
10189 #else
10190   int trigger_player_bits =
10191     (change->actual_trigger_player >= EL_PLAYER_1 &&
10192      change->actual_trigger_player <= EL_PLAYER_4 ?
10193      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10194      PLAYER_BITS_ANY);
10195 #endif
10196
10197   int action_arg_player_bits =
10198     (action_arg >= CA_ARG_PLAYER_1 &&
10199      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10200      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10201      PLAYER_BITS_ANY);
10202
10203   /* ---------- execute action  -------------------------------------------- */
10204
10205   switch (action_type)
10206   {
10207     case CA_NO_ACTION:
10208     {
10209       return;
10210     }
10211
10212     /* ---------- level actions  ------------------------------------------- */
10213
10214     case CA_RESTART_LEVEL:
10215     {
10216       game.restart_level = TRUE;
10217
10218       break;
10219     }
10220
10221     case CA_SHOW_ENVELOPE:
10222     {
10223       int element = getSpecialActionElement(action_arg_element,
10224                                             action_arg_number, EL_ENVELOPE_1);
10225
10226       if (IS_ENVELOPE(element))
10227         local_player->show_envelope = element;
10228
10229       break;
10230     }
10231
10232     case CA_SET_LEVEL_TIME:
10233     {
10234       if (level.time > 0)       /* only modify limited time value */
10235       {
10236         TimeLeft = action_arg_number_new;
10237
10238 #if 1
10239         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10240
10241         DisplayGameControlValues();
10242 #else
10243         DrawGameValue_Time(TimeLeft);
10244 #endif
10245
10246         if (!TimeLeft && setup.time_limit)
10247           for (i = 0; i < MAX_PLAYERS; i++)
10248             KillPlayer(&stored_player[i]);
10249       }
10250
10251       break;
10252     }
10253
10254     case CA_SET_LEVEL_SCORE:
10255     {
10256       local_player->score = action_arg_number_new;
10257
10258 #if 1
10259       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10260
10261       DisplayGameControlValues();
10262 #else
10263       DrawGameValue_Score(local_player->score);
10264 #endif
10265
10266       break;
10267     }
10268
10269     case CA_SET_LEVEL_GEMS:
10270     {
10271       local_player->gems_still_needed = action_arg_number_new;
10272
10273 #if 1
10274       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10275
10276       DisplayGameControlValues();
10277 #else
10278       DrawGameValue_Emeralds(local_player->gems_still_needed);
10279 #endif
10280
10281       break;
10282     }
10283
10284 #if !USE_PLAYER_GRAVITY
10285     case CA_SET_LEVEL_GRAVITY:
10286     {
10287       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10288                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10289                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10290                       game.gravity);
10291       break;
10292     }
10293 #endif
10294
10295     case CA_SET_LEVEL_WIND:
10296     {
10297       game.wind_direction = action_arg_direction;
10298
10299       break;
10300     }
10301
10302     /* ---------- player actions  ------------------------------------------ */
10303
10304     case CA_MOVE_PLAYER:
10305     {
10306       /* automatically move to the next field in specified direction */
10307       for (i = 0; i < MAX_PLAYERS; i++)
10308         if (trigger_player_bits & (1 << i))
10309           stored_player[i].programmed_action = action_arg_direction;
10310
10311       break;
10312     }
10313
10314     case CA_EXIT_PLAYER:
10315     {
10316       for (i = 0; i < MAX_PLAYERS; i++)
10317         if (action_arg_player_bits & (1 << i))
10318           PlayerWins(&stored_player[i]);
10319
10320       break;
10321     }
10322
10323     case CA_KILL_PLAYER:
10324     {
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326         if (action_arg_player_bits & (1 << i))
10327           KillPlayer(&stored_player[i]);
10328
10329       break;
10330     }
10331
10332     case CA_SET_PLAYER_KEYS:
10333     {
10334       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10335       int element = getSpecialActionElement(action_arg_element,
10336                                             action_arg_number, EL_KEY_1);
10337
10338       if (IS_KEY(element))
10339       {
10340         for (i = 0; i < MAX_PLAYERS; i++)
10341         {
10342           if (trigger_player_bits & (1 << i))
10343           {
10344             stored_player[i].key[KEY_NR(element)] = key_state;
10345
10346             DrawGameDoorValues();
10347           }
10348         }
10349       }
10350
10351       break;
10352     }
10353
10354     case CA_SET_PLAYER_SPEED:
10355     {
10356       for (i = 0; i < MAX_PLAYERS; i++)
10357       {
10358         if (trigger_player_bits & (1 << i))
10359         {
10360           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10361
10362           if (action_arg == CA_ARG_SPEED_FASTER &&
10363               stored_player[i].cannot_move)
10364           {
10365             action_arg_number = STEPSIZE_VERY_SLOW;
10366           }
10367           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10368                    action_arg == CA_ARG_SPEED_FASTER)
10369           {
10370             action_arg_number = 2;
10371             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10372                            CA_MODE_MULTIPLY);
10373           }
10374           else if (action_arg == CA_ARG_NUMBER_RESET)
10375           {
10376             action_arg_number = level.initial_player_stepsize[i];
10377           }
10378
10379           move_stepsize =
10380             getModifiedActionNumber(move_stepsize,
10381                                     action_mode,
10382                                     action_arg_number,
10383                                     action_arg_number_min,
10384                                     action_arg_number_max);
10385
10386           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_SHIELD:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           if (action_arg == CA_ARG_SHIELD_OFF)
10400           {
10401             stored_player[i].shield_normal_time_left = 0;
10402             stored_player[i].shield_deadly_time_left = 0;
10403           }
10404           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10405           {
10406             stored_player[i].shield_normal_time_left = 999999;
10407           }
10408           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10409           {
10410             stored_player[i].shield_normal_time_left = 999999;
10411             stored_player[i].shield_deadly_time_left = 999999;
10412           }
10413         }
10414       }
10415
10416       break;
10417     }
10418
10419 #if USE_PLAYER_GRAVITY
10420     case CA_SET_PLAYER_GRAVITY:
10421     {
10422       for (i = 0; i < MAX_PLAYERS; i++)
10423       {
10424         if (trigger_player_bits & (1 << i))
10425         {
10426           stored_player[i].gravity =
10427             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10428              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10429              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10430              stored_player[i].gravity);
10431         }
10432       }
10433
10434       break;
10435     }
10436 #endif
10437
10438     case CA_SET_PLAYER_ARTWORK:
10439     {
10440       for (i = 0; i < MAX_PLAYERS; i++)
10441       {
10442         if (trigger_player_bits & (1 << i))
10443         {
10444           int artwork_element = action_arg_element;
10445
10446           if (action_arg == CA_ARG_ELEMENT_RESET)
10447             artwork_element =
10448               (level.use_artwork_element[i] ? level.artwork_element[i] :
10449                stored_player[i].element_nr);
10450
10451 #if USE_GFX_RESET_PLAYER_ARTWORK
10452           if (stored_player[i].artwork_element != artwork_element)
10453             stored_player[i].Frame = 0;
10454 #endif
10455
10456           stored_player[i].artwork_element = artwork_element;
10457
10458           SetPlayerWaiting(&stored_player[i], FALSE);
10459
10460           /* set number of special actions for bored and sleeping animation */
10461           stored_player[i].num_special_action_bored =
10462             get_num_special_action(artwork_element,
10463                                    ACTION_BORING_1, ACTION_BORING_LAST);
10464           stored_player[i].num_special_action_sleeping =
10465             get_num_special_action(artwork_element,
10466                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10467         }
10468       }
10469
10470       break;
10471     }
10472
10473     /* ---------- CE actions  ---------------------------------------------- */
10474
10475     case CA_SET_CE_VALUE:
10476     {
10477 #if USE_NEW_CUSTOM_VALUE
10478       int last_ce_value = CustomValue[x][y];
10479
10480       CustomValue[x][y] = action_arg_number_new;
10481
10482       if (CustomValue[x][y] != last_ce_value)
10483       {
10484         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10485         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10486
10487         if (CustomValue[x][y] == 0)
10488         {
10489           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10490           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10491         }
10492       }
10493 #endif
10494
10495       break;
10496     }
10497
10498     case CA_SET_CE_SCORE:
10499     {
10500 #if USE_NEW_CUSTOM_VALUE
10501       int last_ce_score = ei->collect_score;
10502
10503       ei->collect_score = action_arg_number_new;
10504
10505       if (ei->collect_score != last_ce_score)
10506       {
10507         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10508         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10509
10510         if (ei->collect_score == 0)
10511         {
10512           int xx, yy;
10513
10514           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10515           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10516
10517           /*
10518             This is a very special case that seems to be a mixture between
10519             CheckElementChange() and CheckTriggeredElementChange(): while
10520             the first one only affects single elements that are triggered
10521             directly, the second one affects multiple elements in the playfield
10522             that are triggered indirectly by another element. This is a third
10523             case: Changing the CE score always affects multiple identical CEs,
10524             so every affected CE must be checked, not only the single CE for
10525             which the CE score was changed in the first place (as every instance
10526             of that CE shares the same CE score, and therefore also can change)!
10527           */
10528           SCAN_PLAYFIELD(xx, yy)
10529           {
10530             if (Feld[xx][yy] == element)
10531               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10532                                  CE_SCORE_GETS_ZERO);
10533           }
10534         }
10535       }
10536 #endif
10537
10538       break;
10539     }
10540
10541     /* ---------- engine actions  ------------------------------------------ */
10542
10543     case CA_SET_ENGINE_SCAN_MODE:
10544     {
10545       InitPlayfieldScanMode(action_arg);
10546
10547       break;
10548     }
10549
10550     default:
10551       break;
10552   }
10553 }
10554
10555 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10556 {
10557   int old_element = Feld[x][y];
10558   int new_element = GetElementFromGroupElement(element);
10559   int previous_move_direction = MovDir[x][y];
10560 #if USE_NEW_CUSTOM_VALUE
10561   int last_ce_value = CustomValue[x][y];
10562 #endif
10563   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10564   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10565   boolean add_player_onto_element = (new_element_is_player &&
10566 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10567                                      /* this breaks SnakeBite when a snake is
10568                                         halfway through a door that closes */
10569                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10570                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10571 #endif
10572                                      IS_WALKABLE(old_element));
10573
10574 #if 0
10575   /* check if element under the player changes from accessible to unaccessible
10576      (needed for special case of dropping element which then changes) */
10577   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10578       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10579   {
10580     Bang(x, y);
10581
10582     return;
10583   }
10584 #endif
10585
10586   if (!add_player_onto_element)
10587   {
10588     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10589       RemoveMovingField(x, y);
10590     else
10591       RemoveField(x, y);
10592
10593     Feld[x][y] = new_element;
10594
10595 #if !USE_GFX_RESET_GFX_ANIMATION
10596     ResetGfxAnimation(x, y);
10597     ResetRandomAnimationValue(x, y);
10598 #endif
10599
10600     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10601       MovDir[x][y] = previous_move_direction;
10602
10603 #if USE_NEW_CUSTOM_VALUE
10604     if (element_info[new_element].use_last_ce_value)
10605       CustomValue[x][y] = last_ce_value;
10606 #endif
10607
10608     InitField_WithBug1(x, y, FALSE);
10609
10610     new_element = Feld[x][y];   /* element may have changed */
10611
10612 #if USE_GFX_RESET_GFX_ANIMATION
10613     ResetGfxAnimation(x, y);
10614     ResetRandomAnimationValue(x, y);
10615 #endif
10616
10617     TEST_DrawLevelField(x, y);
10618
10619     if (GFX_CRUMBLED(new_element))
10620       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10621   }
10622
10623 #if 1
10624   /* check if element under the player changes from accessible to unaccessible
10625      (needed for special case of dropping element which then changes) */
10626   /* (must be checked after creating new element for walkable group elements) */
10627 #if USE_FIX_KILLED_BY_NON_WALKABLE
10628   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10629       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10630   {
10631     Bang(x, y);
10632
10633     return;
10634   }
10635 #else
10636   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10637       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10638   {
10639     Bang(x, y);
10640
10641     return;
10642   }
10643 #endif
10644 #endif
10645
10646   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10647   if (new_element_is_player)
10648     RelocatePlayer(x, y, new_element);
10649
10650   if (is_change)
10651     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10652
10653   TestIfBadThingTouchesPlayer(x, y);
10654   TestIfPlayerTouchesCustomElement(x, y);
10655   TestIfElementTouchesCustomElement(x, y);
10656 }
10657
10658 static void CreateField(int x, int y, int element)
10659 {
10660   CreateFieldExt(x, y, element, FALSE);
10661 }
10662
10663 static void CreateElementFromChange(int x, int y, int element)
10664 {
10665   element = GET_VALID_RUNTIME_ELEMENT(element);
10666
10667 #if USE_STOP_CHANGED_ELEMENTS
10668   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10669   {
10670     int old_element = Feld[x][y];
10671
10672     /* prevent changed element from moving in same engine frame
10673        unless both old and new element can either fall or move */
10674     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10675         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10676       Stop[x][y] = TRUE;
10677   }
10678 #endif
10679
10680   CreateFieldExt(x, y, element, TRUE);
10681 }
10682
10683 static boolean ChangeElement(int x, int y, int element, int page)
10684 {
10685   struct ElementInfo *ei = &element_info[element];
10686   struct ElementChangeInfo *change = &ei->change_page[page];
10687   int ce_value = CustomValue[x][y];
10688   int ce_score = ei->collect_score;
10689   int target_element;
10690   int old_element = Feld[x][y];
10691
10692   /* always use default change event to prevent running into a loop */
10693   if (ChangeEvent[x][y] == -1)
10694     ChangeEvent[x][y] = CE_DELAY;
10695
10696   if (ChangeEvent[x][y] == CE_DELAY)
10697   {
10698     /* reset actual trigger element, trigger player and action element */
10699     change->actual_trigger_element = EL_EMPTY;
10700     change->actual_trigger_player = EL_PLAYER_1;
10701     change->actual_trigger_player_bits = CH_PLAYER_1;
10702     change->actual_trigger_side = CH_SIDE_NONE;
10703     change->actual_trigger_ce_value = 0;
10704     change->actual_trigger_ce_score = 0;
10705   }
10706
10707   /* do not change elements more than a specified maximum number of changes */
10708   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10709     return FALSE;
10710
10711   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10712
10713   if (change->explode)
10714   {
10715     Bang(x, y);
10716
10717     return TRUE;
10718   }
10719
10720   if (change->use_target_content)
10721   {
10722     boolean complete_replace = TRUE;
10723     boolean can_replace[3][3];
10724     int xx, yy;
10725
10726     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10727     {
10728       boolean is_empty;
10729       boolean is_walkable;
10730       boolean is_diggable;
10731       boolean is_collectible;
10732       boolean is_removable;
10733       boolean is_destructible;
10734       int ex = x + xx - 1;
10735       int ey = y + yy - 1;
10736       int content_element = change->target_content.e[xx][yy];
10737       int e;
10738
10739       can_replace[xx][yy] = TRUE;
10740
10741       if (ex == x && ey == y)   /* do not check changing element itself */
10742         continue;
10743
10744       if (content_element == EL_EMPTY_SPACE)
10745       {
10746         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10747
10748         continue;
10749       }
10750
10751       if (!IN_LEV_FIELD(ex, ey))
10752       {
10753         can_replace[xx][yy] = FALSE;
10754         complete_replace = FALSE;
10755
10756         continue;
10757       }
10758
10759       e = Feld[ex][ey];
10760
10761       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10762         e = MovingOrBlocked2Element(ex, ey);
10763
10764       is_empty = (IS_FREE(ex, ey) ||
10765                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10766
10767       is_walkable     = (is_empty || IS_WALKABLE(e));
10768       is_diggable     = (is_empty || IS_DIGGABLE(e));
10769       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10770       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10771       is_removable    = (is_diggable || is_collectible);
10772
10773       can_replace[xx][yy] =
10774         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10775           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10776           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10777           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10778           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10779           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10780          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10781
10782       if (!can_replace[xx][yy])
10783         complete_replace = FALSE;
10784     }
10785
10786     if (!change->only_if_complete || complete_replace)
10787     {
10788       boolean something_has_changed = FALSE;
10789
10790       if (change->only_if_complete && change->use_random_replace &&
10791           RND(100) < change->random_percentage)
10792         return FALSE;
10793
10794       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10795       {
10796         int ex = x + xx - 1;
10797         int ey = y + yy - 1;
10798         int content_element;
10799
10800         if (can_replace[xx][yy] && (!change->use_random_replace ||
10801                                     RND(100) < change->random_percentage))
10802         {
10803           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10804             RemoveMovingField(ex, ey);
10805
10806           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10807
10808           content_element = change->target_content.e[xx][yy];
10809           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10810                                               ce_value, ce_score);
10811
10812           CreateElementFromChange(ex, ey, target_element);
10813
10814           something_has_changed = TRUE;
10815
10816           /* for symmetry reasons, freeze newly created border elements */
10817           if (ex != x || ey != y)
10818             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10819         }
10820       }
10821
10822       if (something_has_changed)
10823       {
10824         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10825         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10826       }
10827     }
10828   }
10829   else
10830   {
10831     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10832                                         ce_value, ce_score);
10833
10834     if (element == EL_DIAGONAL_GROWING ||
10835         element == EL_DIAGONAL_SHRINKING)
10836     {
10837       target_element = Store[x][y];
10838
10839       Store[x][y] = EL_EMPTY;
10840     }
10841
10842     CreateElementFromChange(x, y, target_element);
10843
10844     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10845     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10846   }
10847
10848   /* this uses direct change before indirect change */
10849   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10850
10851   return TRUE;
10852 }
10853
10854 #if USE_NEW_DELAYED_ACTION
10855
10856 static void HandleElementChange(int x, int y, int page)
10857 {
10858   int element = MovingOrBlocked2Element(x, y);
10859   struct ElementInfo *ei = &element_info[element];
10860   struct ElementChangeInfo *change = &ei->change_page[page];
10861
10862 #ifdef DEBUG
10863   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10864       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10865   {
10866     printf("\n\n");
10867     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10868            x, y, element, element_info[element].token_name);
10869     printf("HandleElementChange(): This should never happen!\n");
10870     printf("\n\n");
10871   }
10872 #endif
10873
10874   /* this can happen with classic bombs on walkable, changing elements */
10875   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10876   {
10877 #if 0
10878     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10879       ChangeDelay[x][y] = 0;
10880 #endif
10881
10882     return;
10883   }
10884
10885   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10886   {
10887     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10888
10889     if (change->can_change)
10890     {
10891 #if 1
10892       /* !!! not clear why graphic animation should be reset at all here !!! */
10893       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10894 #if USE_GFX_RESET_WHEN_NOT_MOVING
10895       /* when a custom element is about to change (for example by change delay),
10896          do not reset graphic animation when the custom element is moving */
10897       if (!IS_MOVING(x, y))
10898 #endif
10899       {
10900         ResetGfxAnimation(x, y);
10901         ResetRandomAnimationValue(x, y);
10902       }
10903 #endif
10904
10905       if (change->pre_change_function)
10906         change->pre_change_function(x, y);
10907     }
10908   }
10909
10910   ChangeDelay[x][y]--;
10911
10912   if (ChangeDelay[x][y] != 0)           /* continue element change */
10913   {
10914     if (change->can_change)
10915     {
10916       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10917
10918       if (IS_ANIMATED(graphic))
10919         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10920
10921       if (change->change_function)
10922         change->change_function(x, y);
10923     }
10924   }
10925   else                                  /* finish element change */
10926   {
10927     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10928     {
10929       page = ChangePage[x][y];
10930       ChangePage[x][y] = -1;
10931
10932       change = &ei->change_page[page];
10933     }
10934
10935     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10936     {
10937       ChangeDelay[x][y] = 1;            /* try change after next move step */
10938       ChangePage[x][y] = page;          /* remember page to use for change */
10939
10940       return;
10941     }
10942
10943     if (change->can_change)
10944     {
10945       if (ChangeElement(x, y, element, page))
10946       {
10947         if (change->post_change_function)
10948           change->post_change_function(x, y);
10949       }
10950     }
10951
10952     if (change->has_action)
10953       ExecuteCustomElementAction(x, y, element, page);
10954   }
10955 }
10956
10957 #else
10958
10959 static void HandleElementChange(int x, int y, int page)
10960 {
10961   int element = MovingOrBlocked2Element(x, y);
10962   struct ElementInfo *ei = &element_info[element];
10963   struct ElementChangeInfo *change = &ei->change_page[page];
10964
10965 #ifdef DEBUG
10966   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10967   {
10968     printf("\n\n");
10969     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10970            x, y, element, element_info[element].token_name);
10971     printf("HandleElementChange(): This should never happen!\n");
10972     printf("\n\n");
10973   }
10974 #endif
10975
10976   /* this can happen with classic bombs on walkable, changing elements */
10977   if (!CAN_CHANGE(element))
10978   {
10979 #if 0
10980     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10981       ChangeDelay[x][y] = 0;
10982 #endif
10983
10984     return;
10985   }
10986
10987   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10988   {
10989     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10990
10991     ResetGfxAnimation(x, y);
10992     ResetRandomAnimationValue(x, y);
10993
10994     if (change->pre_change_function)
10995       change->pre_change_function(x, y);
10996   }
10997
10998   ChangeDelay[x][y]--;
10999
11000   if (ChangeDelay[x][y] != 0)           /* continue element change */
11001   {
11002     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11003
11004     if (IS_ANIMATED(graphic))
11005       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11006
11007     if (change->change_function)
11008       change->change_function(x, y);
11009   }
11010   else                                  /* finish element change */
11011   {
11012     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11013     {
11014       page = ChangePage[x][y];
11015       ChangePage[x][y] = -1;
11016
11017       change = &ei->change_page[page];
11018     }
11019
11020     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11021     {
11022       ChangeDelay[x][y] = 1;            /* try change after next move step */
11023       ChangePage[x][y] = page;          /* remember page to use for change */
11024
11025       return;
11026     }
11027
11028     if (ChangeElement(x, y, element, page))
11029     {
11030       if (change->post_change_function)
11031         change->post_change_function(x, y);
11032     }
11033   }
11034 }
11035
11036 #endif
11037
11038 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11039                                               int trigger_element,
11040                                               int trigger_event,
11041                                               int trigger_player,
11042                                               int trigger_side,
11043                                               int trigger_page)
11044 {
11045   boolean change_done_any = FALSE;
11046   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11047   int i;
11048
11049   if (!(trigger_events[trigger_element][trigger_event]))
11050     return FALSE;
11051
11052 #if 0
11053   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11054          trigger_event, recursion_loop_depth, recursion_loop_detected,
11055          recursion_loop_element, EL_NAME(recursion_loop_element));
11056 #endif
11057
11058   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11059
11060   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11061   {
11062     int element = EL_CUSTOM_START + i;
11063     boolean change_done = FALSE;
11064     int p;
11065
11066     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11067         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11068       continue;
11069
11070     for (p = 0; p < element_info[element].num_change_pages; p++)
11071     {
11072       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11073
11074       if (change->can_change_or_has_action &&
11075           change->has_event[trigger_event] &&
11076           change->trigger_side & trigger_side &&
11077           change->trigger_player & trigger_player &&
11078           change->trigger_page & trigger_page_bits &&
11079           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11080       {
11081         change->actual_trigger_element = trigger_element;
11082         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11083         change->actual_trigger_player_bits = trigger_player;
11084         change->actual_trigger_side = trigger_side;
11085         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11086         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11087
11088         if ((change->can_change && !change_done) || change->has_action)
11089         {
11090           int x, y;
11091
11092           SCAN_PLAYFIELD(x, y)
11093           {
11094             if (Feld[x][y] == element)
11095             {
11096               if (change->can_change && !change_done)
11097               {
11098                 ChangeDelay[x][y] = 1;
11099                 ChangeEvent[x][y] = trigger_event;
11100
11101                 HandleElementChange(x, y, p);
11102               }
11103 #if USE_NEW_DELAYED_ACTION
11104               else if (change->has_action)
11105               {
11106                 ExecuteCustomElementAction(x, y, element, p);
11107                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11108               }
11109 #else
11110               if (change->has_action)
11111               {
11112                 ExecuteCustomElementAction(x, y, element, p);
11113                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11114               }
11115 #endif
11116             }
11117           }
11118
11119           if (change->can_change)
11120           {
11121             change_done = TRUE;
11122             change_done_any = TRUE;
11123           }
11124         }
11125       }
11126     }
11127   }
11128
11129   RECURSION_LOOP_DETECTION_END();
11130
11131   return change_done_any;
11132 }
11133
11134 static boolean CheckElementChangeExt(int x, int y,
11135                                      int element,
11136                                      int trigger_element,
11137                                      int trigger_event,
11138                                      int trigger_player,
11139                                      int trigger_side)
11140 {
11141   boolean change_done = FALSE;
11142   int p;
11143
11144   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11145       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11146     return FALSE;
11147
11148   if (Feld[x][y] == EL_BLOCKED)
11149   {
11150     Blocked2Moving(x, y, &x, &y);
11151     element = Feld[x][y];
11152   }
11153
11154 #if 0
11155   /* check if element has already changed */
11156   if (Feld[x][y] != element)
11157     return FALSE;
11158 #else
11159   /* check if element has already changed or is about to change after moving */
11160   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11161        Feld[x][y] != element) ||
11162
11163       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11164        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11165         ChangePage[x][y] != -1)))
11166     return FALSE;
11167 #endif
11168
11169 #if 0
11170   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11171          trigger_event, recursion_loop_depth, recursion_loop_detected,
11172          recursion_loop_element, EL_NAME(recursion_loop_element));
11173 #endif
11174
11175   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11176
11177   for (p = 0; p < element_info[element].num_change_pages; p++)
11178   {
11179     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11180
11181     /* check trigger element for all events where the element that is checked
11182        for changing interacts with a directly adjacent element -- this is
11183        different to element changes that affect other elements to change on the
11184        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11185     boolean check_trigger_element =
11186       (trigger_event == CE_TOUCHING_X ||
11187        trigger_event == CE_HITTING_X ||
11188        trigger_event == CE_HIT_BY_X ||
11189 #if 1
11190        /* this one was forgotten until 3.2.3 */
11191        trigger_event == CE_DIGGING_X);
11192 #endif
11193
11194     if (change->can_change_or_has_action &&
11195         change->has_event[trigger_event] &&
11196         change->trigger_side & trigger_side &&
11197         change->trigger_player & trigger_player &&
11198         (!check_trigger_element ||
11199          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11200     {
11201       change->actual_trigger_element = trigger_element;
11202       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11203       change->actual_trigger_player_bits = trigger_player;
11204       change->actual_trigger_side = trigger_side;
11205       change->actual_trigger_ce_value = CustomValue[x][y];
11206       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11207
11208       /* special case: trigger element not at (x,y) position for some events */
11209       if (check_trigger_element)
11210       {
11211         static struct
11212         {
11213           int dx, dy;
11214         } move_xy[] =
11215           {
11216             {  0,  0 },
11217             { -1,  0 },
11218             { +1,  0 },
11219             {  0,  0 },
11220             {  0, -1 },
11221             {  0,  0 }, { 0, 0 }, { 0, 0 },
11222             {  0, +1 }
11223           };
11224
11225         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11226         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11227
11228         change->actual_trigger_ce_value = CustomValue[xx][yy];
11229         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11230       }
11231
11232       if (change->can_change && !change_done)
11233       {
11234         ChangeDelay[x][y] = 1;
11235         ChangeEvent[x][y] = trigger_event;
11236
11237         HandleElementChange(x, y, p);
11238
11239         change_done = TRUE;
11240       }
11241 #if USE_NEW_DELAYED_ACTION
11242       else if (change->has_action)
11243       {
11244         ExecuteCustomElementAction(x, y, element, p);
11245         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11246       }
11247 #else
11248       if (change->has_action)
11249       {
11250         ExecuteCustomElementAction(x, y, element, p);
11251         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11252       }
11253 #endif
11254     }
11255   }
11256
11257   RECURSION_LOOP_DETECTION_END();
11258
11259   return change_done;
11260 }
11261
11262 static void PlayPlayerSound(struct PlayerInfo *player)
11263 {
11264   int jx = player->jx, jy = player->jy;
11265   int sound_element = player->artwork_element;
11266   int last_action = player->last_action_waiting;
11267   int action = player->action_waiting;
11268
11269   if (player->is_waiting)
11270   {
11271     if (action != last_action)
11272       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11273     else
11274       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11275   }
11276   else
11277   {
11278     if (action != last_action)
11279       StopSound(element_info[sound_element].sound[last_action]);
11280
11281     if (last_action == ACTION_SLEEPING)
11282       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11283   }
11284 }
11285
11286 static void PlayAllPlayersSound()
11287 {
11288   int i;
11289
11290   for (i = 0; i < MAX_PLAYERS; i++)
11291     if (stored_player[i].active)
11292       PlayPlayerSound(&stored_player[i]);
11293 }
11294
11295 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11296 {
11297   boolean last_waiting = player->is_waiting;
11298   int move_dir = player->MovDir;
11299
11300   player->dir_waiting = move_dir;
11301   player->last_action_waiting = player->action_waiting;
11302
11303   if (is_waiting)
11304   {
11305     if (!last_waiting)          /* not waiting -> waiting */
11306     {
11307       player->is_waiting = TRUE;
11308
11309       player->frame_counter_bored =
11310         FrameCounter +
11311         game.player_boring_delay_fixed +
11312         GetSimpleRandom(game.player_boring_delay_random);
11313       player->frame_counter_sleeping =
11314         FrameCounter +
11315         game.player_sleeping_delay_fixed +
11316         GetSimpleRandom(game.player_sleeping_delay_random);
11317
11318       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11319     }
11320
11321     if (game.player_sleeping_delay_fixed +
11322         game.player_sleeping_delay_random > 0 &&
11323         player->anim_delay_counter == 0 &&
11324         player->post_delay_counter == 0 &&
11325         FrameCounter >= player->frame_counter_sleeping)
11326       player->is_sleeping = TRUE;
11327     else if (game.player_boring_delay_fixed +
11328              game.player_boring_delay_random > 0 &&
11329              FrameCounter >= player->frame_counter_bored)
11330       player->is_bored = TRUE;
11331
11332     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11333                               player->is_bored ? ACTION_BORING :
11334                               ACTION_WAITING);
11335
11336     if (player->is_sleeping && player->use_murphy)
11337     {
11338       /* special case for sleeping Murphy when leaning against non-free tile */
11339
11340       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11341           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11342            !IS_MOVING(player->jx - 1, player->jy)))
11343         move_dir = MV_LEFT;
11344       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11345                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11346                 !IS_MOVING(player->jx + 1, player->jy)))
11347         move_dir = MV_RIGHT;
11348       else
11349         player->is_sleeping = FALSE;
11350
11351       player->dir_waiting = move_dir;
11352     }
11353
11354     if (player->is_sleeping)
11355     {
11356       if (player->num_special_action_sleeping > 0)
11357       {
11358         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11359         {
11360           int last_special_action = player->special_action_sleeping;
11361           int num_special_action = player->num_special_action_sleeping;
11362           int special_action =
11363             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11364              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11365              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11366              last_special_action + 1 : ACTION_SLEEPING);
11367           int special_graphic =
11368             el_act_dir2img(player->artwork_element, special_action, move_dir);
11369
11370           player->anim_delay_counter =
11371             graphic_info[special_graphic].anim_delay_fixed +
11372             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11373           player->post_delay_counter =
11374             graphic_info[special_graphic].post_delay_fixed +
11375             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11376
11377           player->special_action_sleeping = special_action;
11378         }
11379
11380         if (player->anim_delay_counter > 0)
11381         {
11382           player->action_waiting = player->special_action_sleeping;
11383           player->anim_delay_counter--;
11384         }
11385         else if (player->post_delay_counter > 0)
11386         {
11387           player->post_delay_counter--;
11388         }
11389       }
11390     }
11391     else if (player->is_bored)
11392     {
11393       if (player->num_special_action_bored > 0)
11394       {
11395         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11396         {
11397           int special_action =
11398             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11399           int special_graphic =
11400             el_act_dir2img(player->artwork_element, special_action, move_dir);
11401
11402           player->anim_delay_counter =
11403             graphic_info[special_graphic].anim_delay_fixed +
11404             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11405           player->post_delay_counter =
11406             graphic_info[special_graphic].post_delay_fixed +
11407             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11408
11409           player->special_action_bored = special_action;
11410         }
11411
11412         if (player->anim_delay_counter > 0)
11413         {
11414           player->action_waiting = player->special_action_bored;
11415           player->anim_delay_counter--;
11416         }
11417         else if (player->post_delay_counter > 0)
11418         {
11419           player->post_delay_counter--;
11420         }
11421       }
11422     }
11423   }
11424   else if (last_waiting)        /* waiting -> not waiting */
11425   {
11426     player->is_waiting = FALSE;
11427     player->is_bored = FALSE;
11428     player->is_sleeping = FALSE;
11429
11430     player->frame_counter_bored = -1;
11431     player->frame_counter_sleeping = -1;
11432
11433     player->anim_delay_counter = 0;
11434     player->post_delay_counter = 0;
11435
11436     player->dir_waiting = player->MovDir;
11437     player->action_waiting = ACTION_DEFAULT;
11438
11439     player->special_action_bored = ACTION_DEFAULT;
11440     player->special_action_sleeping = ACTION_DEFAULT;
11441   }
11442 }
11443
11444 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11445 {
11446   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11447   int left      = player_action & JOY_LEFT;
11448   int right     = player_action & JOY_RIGHT;
11449   int up        = player_action & JOY_UP;
11450   int down      = player_action & JOY_DOWN;
11451   int button1   = player_action & JOY_BUTTON_1;
11452   int button2   = player_action & JOY_BUTTON_2;
11453   int dx        = (left ? -1 : right ? 1 : 0);
11454   int dy        = (up   ? -1 : down  ? 1 : 0);
11455
11456   if (!player->active || tape.pausing)
11457     return 0;
11458
11459   if (player_action)
11460   {
11461     if (button1)
11462       snapped = SnapField(player, dx, dy);
11463     else
11464     {
11465       if (button2)
11466         dropped = DropElement(player);
11467
11468       moved = MovePlayer(player, dx, dy);
11469     }
11470
11471     if (tape.single_step && tape.recording && !tape.pausing)
11472     {
11473       if (button1 || (dropped && !moved))
11474       {
11475         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11476         SnapField(player, 0, 0);                /* stop snapping */
11477       }
11478     }
11479
11480     SetPlayerWaiting(player, FALSE);
11481
11482     return player_action;
11483   }
11484   else
11485   {
11486     /* no actions for this player (no input at player's configured device) */
11487
11488     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11489     SnapField(player, 0, 0);
11490     CheckGravityMovementWhenNotMoving(player);
11491
11492     if (player->MovPos == 0)
11493       SetPlayerWaiting(player, TRUE);
11494
11495     if (player->MovPos == 0)    /* needed for tape.playing */
11496       player->is_moving = FALSE;
11497
11498     player->is_dropping = FALSE;
11499     player->is_dropping_pressed = FALSE;
11500     player->drop_pressed_delay = 0;
11501
11502     return 0;
11503   }
11504 }
11505
11506 static void CheckLevelTime()
11507 {
11508   int i;
11509
11510   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11511   {
11512     if (level.native_em_level->lev->home == 0)  /* all players at home */
11513     {
11514       PlayerWins(local_player);
11515
11516       AllPlayersGone = TRUE;
11517
11518       level.native_em_level->lev->home = -1;
11519     }
11520
11521     if (level.native_em_level->ply[0]->alive == 0 &&
11522         level.native_em_level->ply[1]->alive == 0 &&
11523         level.native_em_level->ply[2]->alive == 0 &&
11524         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11525       AllPlayersGone = TRUE;
11526   }
11527
11528   if (TimeFrames >= FRAMES_PER_SECOND)
11529   {
11530     TimeFrames = 0;
11531     TapeTime++;
11532
11533     for (i = 0; i < MAX_PLAYERS; i++)
11534     {
11535       struct PlayerInfo *player = &stored_player[i];
11536
11537       if (SHIELD_ON(player))
11538       {
11539         player->shield_normal_time_left--;
11540
11541         if (player->shield_deadly_time_left > 0)
11542           player->shield_deadly_time_left--;
11543       }
11544     }
11545
11546     if (!local_player->LevelSolved && !level.use_step_counter)
11547     {
11548       TimePlayed++;
11549
11550       if (TimeLeft > 0)
11551       {
11552         TimeLeft--;
11553
11554         if (TimeLeft <= 10 && setup.time_limit)
11555           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11556
11557 #if 1
11558         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11559
11560         DisplayGameControlValues();
11561 #else
11562         DrawGameValue_Time(TimeLeft);
11563 #endif
11564
11565         if (!TimeLeft && setup.time_limit)
11566         {
11567           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11568             level.native_em_level->lev->killed_out_of_time = TRUE;
11569           else
11570             for (i = 0; i < MAX_PLAYERS; i++)
11571               KillPlayer(&stored_player[i]);
11572         }
11573       }
11574 #if 1
11575       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11576       {
11577         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11578
11579         DisplayGameControlValues();
11580       }
11581 #else
11582       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11583         DrawGameValue_Time(TimePlayed);
11584 #endif
11585
11586       level.native_em_level->lev->time =
11587         (level.time == 0 ? TimePlayed : TimeLeft);
11588     }
11589
11590     if (tape.recording || tape.playing)
11591       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11592   }
11593
11594 #if 1
11595   UpdateAndDisplayGameControlValues();
11596 #else
11597   UpdateGameDoorValues();
11598   DrawGameDoorValues();
11599 #endif
11600 }
11601
11602 void AdvanceFrameAndPlayerCounters(int player_nr)
11603 {
11604   int i;
11605
11606   /* advance frame counters (global frame counter and time frame counter) */
11607   FrameCounter++;
11608   TimeFrames++;
11609
11610   /* advance player counters (counters for move delay, move animation etc.) */
11611   for (i = 0; i < MAX_PLAYERS; i++)
11612   {
11613     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11614     int move_delay_value = stored_player[i].move_delay_value;
11615     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11616
11617     if (!advance_player_counters)       /* not all players may be affected */
11618       continue;
11619
11620 #if USE_NEW_PLAYER_ANIM
11621     if (move_frames == 0)       /* less than one move per game frame */
11622     {
11623       int stepsize = TILEX / move_delay_value;
11624       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11625       int count = (stored_player[i].is_moving ?
11626                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11627
11628       if (count % delay == 0)
11629         move_frames = 1;
11630     }
11631 #endif
11632
11633     stored_player[i].Frame += move_frames;
11634
11635     if (stored_player[i].MovPos != 0)
11636       stored_player[i].StepFrame += move_frames;
11637
11638     if (stored_player[i].move_delay > 0)
11639       stored_player[i].move_delay--;
11640
11641     /* due to bugs in previous versions, counter must count up, not down */
11642     if (stored_player[i].push_delay != -1)
11643       stored_player[i].push_delay++;
11644
11645     if (stored_player[i].drop_delay > 0)
11646       stored_player[i].drop_delay--;
11647
11648     if (stored_player[i].is_dropping_pressed)
11649       stored_player[i].drop_pressed_delay++;
11650   }
11651 }
11652
11653 void StartGameActions(boolean init_network_game, boolean record_tape,
11654                       long random_seed)
11655 {
11656   unsigned long new_random_seed = InitRND(random_seed);
11657
11658   if (record_tape)
11659     TapeStartRecording(new_random_seed);
11660
11661 #if defined(NETWORK_AVALIABLE)
11662   if (init_network_game)
11663   {
11664     SendToServer_StartPlaying();
11665
11666     return;
11667   }
11668 #endif
11669
11670   InitGame();
11671 }
11672
11673 void GameActions()
11674 {
11675   static unsigned long game_frame_delay = 0;
11676   unsigned long game_frame_delay_value;
11677   byte *recorded_player_action;
11678   byte summarized_player_action = 0;
11679   byte tape_action[MAX_PLAYERS];
11680   int i;
11681
11682   /* detect endless loops, caused by custom element programming */
11683   if (recursion_loop_detected && recursion_loop_depth == 0)
11684   {
11685     char *message = getStringCat3("Internal Error ! Element ",
11686                                   EL_NAME(recursion_loop_element),
11687                                   " caused endless loop ! Quit the game ?");
11688
11689     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11690           EL_NAME(recursion_loop_element));
11691
11692     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11693
11694     recursion_loop_detected = FALSE;    /* if game should be continued */
11695
11696     free(message);
11697
11698     return;
11699   }
11700
11701   if (game.restart_level)
11702     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11703
11704   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11705   {
11706     if (level.native_em_level->lev->home == 0)  /* all players at home */
11707     {
11708       PlayerWins(local_player);
11709
11710       AllPlayersGone = TRUE;
11711
11712       level.native_em_level->lev->home = -1;
11713     }
11714
11715     if (level.native_em_level->ply[0]->alive == 0 &&
11716         level.native_em_level->ply[1]->alive == 0 &&
11717         level.native_em_level->ply[2]->alive == 0 &&
11718         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11719       AllPlayersGone = TRUE;
11720   }
11721
11722   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11723     GameWon();
11724
11725   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11726     TapeStop();
11727
11728   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11729     return;
11730
11731   game_frame_delay_value =
11732     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11733
11734   if (tape.playing && tape.warp_forward && !tape.pausing)
11735     game_frame_delay_value = 0;
11736
11737   /* ---------- main game synchronization point ---------- */
11738
11739   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11740
11741   if (network_playing && !network_player_action_received)
11742   {
11743     /* try to get network player actions in time */
11744
11745 #if defined(NETWORK_AVALIABLE)
11746     /* last chance to get network player actions without main loop delay */
11747     HandleNetworking();
11748 #endif
11749
11750     /* game was quit by network peer */
11751     if (game_status != GAME_MODE_PLAYING)
11752       return;
11753
11754     if (!network_player_action_received)
11755       return;           /* failed to get network player actions in time */
11756
11757     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11758   }
11759
11760   if (tape.pausing)
11761     return;
11762
11763   /* at this point we know that we really continue executing the game */
11764
11765   network_player_action_received = FALSE;
11766
11767   /* when playing tape, read previously recorded player input from tape data */
11768   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11769
11770 #if 1
11771   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11772   if (tape.pausing)
11773     return;
11774 #endif
11775
11776   if (tape.set_centered_player)
11777   {
11778     game.centered_player_nr_next = tape.centered_player_nr_next;
11779     game.set_centered_player = TRUE;
11780   }
11781
11782   for (i = 0; i < MAX_PLAYERS; i++)
11783   {
11784     summarized_player_action |= stored_player[i].action;
11785
11786     if (!network_playing)
11787       stored_player[i].effective_action = stored_player[i].action;
11788   }
11789
11790 #if defined(NETWORK_AVALIABLE)
11791   if (network_playing)
11792     SendToServer_MovePlayer(summarized_player_action);
11793 #endif
11794
11795   if (!options.network && !setup.team_mode)
11796     local_player->effective_action = summarized_player_action;
11797
11798   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11799   {
11800     for (i = 0; i < MAX_PLAYERS; i++)
11801       stored_player[i].effective_action =
11802         (i == game.centered_player_nr ? summarized_player_action : 0);
11803   }
11804
11805   if (recorded_player_action != NULL)
11806     for (i = 0; i < MAX_PLAYERS; i++)
11807       stored_player[i].effective_action = recorded_player_action[i];
11808
11809   for (i = 0; i < MAX_PLAYERS; i++)
11810   {
11811     tape_action[i] = stored_player[i].effective_action;
11812
11813     /* (this can only happen in the R'n'D game engine) */
11814     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11815       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11816   }
11817
11818   /* only record actions from input devices, but not programmed actions */
11819   if (tape.recording)
11820     TapeRecordAction(tape_action);
11821
11822   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11823   {
11824     GameActions_EM_Main();
11825   }
11826   else
11827   {
11828     GameActions_RND();
11829   }
11830 }
11831
11832 void GameActions_EM_Main()
11833 {
11834   byte effective_action[MAX_PLAYERS];
11835   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11836   int i;
11837
11838   for (i = 0; i < MAX_PLAYERS; i++)
11839     effective_action[i] = stored_player[i].effective_action;
11840
11841   GameActions_EM(effective_action, warp_mode);
11842
11843   CheckLevelTime();
11844
11845   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11846 }
11847
11848 void GameActions_RND()
11849 {
11850   int magic_wall_x = 0, magic_wall_y = 0;
11851   int i, x, y, element, graphic;
11852
11853   InitPlayfieldScanModeVars();
11854
11855 #if USE_ONE_MORE_CHANGE_PER_FRAME
11856   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11857   {
11858     SCAN_PLAYFIELD(x, y)
11859     {
11860       ChangeCount[x][y] = 0;
11861       ChangeEvent[x][y] = -1;
11862     }
11863   }
11864 #endif
11865
11866   if (game.set_centered_player)
11867   {
11868     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11869
11870     /* switching to "all players" only possible if all players fit to screen */
11871     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11872     {
11873       game.centered_player_nr_next = game.centered_player_nr;
11874       game.set_centered_player = FALSE;
11875     }
11876
11877     /* do not switch focus to non-existing (or non-active) player */
11878     if (game.centered_player_nr_next >= 0 &&
11879         !stored_player[game.centered_player_nr_next].active)
11880     {
11881       game.centered_player_nr_next = game.centered_player_nr;
11882       game.set_centered_player = FALSE;
11883     }
11884   }
11885
11886   if (game.set_centered_player &&
11887       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11888   {
11889     int sx, sy;
11890
11891     if (game.centered_player_nr_next == -1)
11892     {
11893       setScreenCenteredToAllPlayers(&sx, &sy);
11894     }
11895     else
11896     {
11897       sx = stored_player[game.centered_player_nr_next].jx;
11898       sy = stored_player[game.centered_player_nr_next].jy;
11899     }
11900
11901     game.centered_player_nr = game.centered_player_nr_next;
11902     game.set_centered_player = FALSE;
11903
11904     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11905     DrawGameDoorValues();
11906   }
11907
11908   for (i = 0; i < MAX_PLAYERS; i++)
11909   {
11910     int actual_player_action = stored_player[i].effective_action;
11911
11912 #if 1
11913     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11914        - rnd_equinox_tetrachloride 048
11915        - rnd_equinox_tetrachloride_ii 096
11916        - rnd_emanuel_schmieg 002
11917        - doctor_sloan_ww 001, 020
11918     */
11919     if (stored_player[i].MovPos == 0)
11920       CheckGravityMovement(&stored_player[i]);
11921 #endif
11922
11923     /* overwrite programmed action with tape action */
11924     if (stored_player[i].programmed_action)
11925       actual_player_action = stored_player[i].programmed_action;
11926
11927     PlayerActions(&stored_player[i], actual_player_action);
11928
11929     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11930   }
11931
11932   ScrollScreen(NULL, SCROLL_GO_ON);
11933
11934   /* for backwards compatibility, the following code emulates a fixed bug that
11935      occured when pushing elements (causing elements that just made their last
11936      pushing step to already (if possible) make their first falling step in the
11937      same game frame, which is bad); this code is also needed to use the famous
11938      "spring push bug" which is used in older levels and might be wanted to be
11939      used also in newer levels, but in this case the buggy pushing code is only
11940      affecting the "spring" element and no other elements */
11941
11942   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11943   {
11944     for (i = 0; i < MAX_PLAYERS; i++)
11945     {
11946       struct PlayerInfo *player = &stored_player[i];
11947       int x = player->jx;
11948       int y = player->jy;
11949
11950       if (player->active && player->is_pushing && player->is_moving &&
11951           IS_MOVING(x, y) &&
11952           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11953            Feld[x][y] == EL_SPRING))
11954       {
11955         ContinueMoving(x, y);
11956
11957         /* continue moving after pushing (this is actually a bug) */
11958         if (!IS_MOVING(x, y))
11959           Stop[x][y] = FALSE;
11960       }
11961     }
11962   }
11963
11964 #if 0
11965   debug_print_timestamp(0, "start main loop profiling");
11966 #endif
11967
11968   SCAN_PLAYFIELD(x, y)
11969   {
11970     ChangeCount[x][y] = 0;
11971     ChangeEvent[x][y] = -1;
11972
11973     /* this must be handled before main playfield loop */
11974     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11975     {
11976       MovDelay[x][y]--;
11977       if (MovDelay[x][y] <= 0)
11978         RemoveField(x, y);
11979     }
11980
11981 #if USE_NEW_SNAP_DELAY
11982     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11983     {
11984       MovDelay[x][y]--;
11985       if (MovDelay[x][y] <= 0)
11986       {
11987         RemoveField(x, y);
11988         TEST_DrawLevelField(x, y);
11989
11990         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11991       }
11992     }
11993 #endif
11994
11995 #if DEBUG
11996     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11997     {
11998       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11999       printf("GameActions(): This should never happen!\n");
12000
12001       ChangePage[x][y] = -1;
12002     }
12003 #endif
12004
12005     Stop[x][y] = FALSE;
12006     if (WasJustMoving[x][y] > 0)
12007       WasJustMoving[x][y]--;
12008     if (WasJustFalling[x][y] > 0)
12009       WasJustFalling[x][y]--;
12010     if (CheckCollision[x][y] > 0)
12011       CheckCollision[x][y]--;
12012     if (CheckImpact[x][y] > 0)
12013       CheckImpact[x][y]--;
12014
12015     GfxFrame[x][y]++;
12016
12017     /* reset finished pushing action (not done in ContinueMoving() to allow
12018        continuous pushing animation for elements with zero push delay) */
12019     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12020     {
12021       ResetGfxAnimation(x, y);
12022       TEST_DrawLevelField(x, y);
12023     }
12024
12025 #if DEBUG
12026     if (IS_BLOCKED(x, y))
12027     {
12028       int oldx, oldy;
12029
12030       Blocked2Moving(x, y, &oldx, &oldy);
12031       if (!IS_MOVING(oldx, oldy))
12032       {
12033         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12034         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12035         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12036         printf("GameActions(): This should never happen!\n");
12037       }
12038     }
12039 #endif
12040   }
12041
12042 #if 0
12043   debug_print_timestamp(0, "- time for pre-main loop:");
12044 #endif
12045
12046 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12047   SCAN_PLAYFIELD(x, y)
12048   {
12049     element = Feld[x][y];
12050     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12051
12052 #if 1
12053     {
12054 #if 1
12055       int element2 = element;
12056       int graphic2 = graphic;
12057 #else
12058       int element2 = Feld[x][y];
12059       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12060 #endif
12061       int last_gfx_frame = GfxFrame[x][y];
12062
12063       if (graphic_info[graphic2].anim_global_sync)
12064         GfxFrame[x][y] = FrameCounter;
12065       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12066         GfxFrame[x][y] = CustomValue[x][y];
12067       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12068         GfxFrame[x][y] = element_info[element2].collect_score;
12069       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12070         GfxFrame[x][y] = ChangeDelay[x][y];
12071
12072       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12073         DrawLevelGraphicAnimation(x, y, graphic2);
12074     }
12075 #else
12076     ResetGfxFrame(x, y, TRUE);
12077 #endif
12078
12079 #if 1
12080     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12081         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12082       ResetRandomAnimationValue(x, y);
12083 #endif
12084
12085 #if 1
12086     SetRandomAnimationValue(x, y);
12087 #endif
12088
12089 #if 1
12090     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12091 #endif
12092   }
12093 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12094
12095 #if 0
12096   debug_print_timestamp(0, "- time for TEST loop:     -->");
12097 #endif
12098
12099   SCAN_PLAYFIELD(x, y)
12100   {
12101     element = Feld[x][y];
12102     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12103
12104     ResetGfxFrame(x, y, TRUE);
12105
12106     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12107         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12108       ResetRandomAnimationValue(x, y);
12109
12110     SetRandomAnimationValue(x, y);
12111
12112     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12113
12114     if (IS_INACTIVE(element))
12115     {
12116       if (IS_ANIMATED(graphic))
12117         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12118
12119       continue;
12120     }
12121
12122     /* this may take place after moving, so 'element' may have changed */
12123     if (IS_CHANGING(x, y) &&
12124         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12125     {
12126       int page = element_info[element].event_page_nr[CE_DELAY];
12127
12128 #if 1
12129       HandleElementChange(x, y, page);
12130 #else
12131       if (CAN_CHANGE(element))
12132         HandleElementChange(x, y, page);
12133
12134       if (HAS_ACTION(element))
12135         ExecuteCustomElementAction(x, y, element, page);
12136 #endif
12137
12138       element = Feld[x][y];
12139       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12140     }
12141
12142 #if 0   // ---------------------------------------------------------------------
12143
12144     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12145     {
12146       StartMoving(x, y);
12147
12148       element = Feld[x][y];
12149       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12150
12151       if (IS_ANIMATED(graphic) &&
12152           !IS_MOVING(x, y) &&
12153           !Stop[x][y])
12154         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12155
12156       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12157         TEST_DrawTwinkleOnField(x, y);
12158     }
12159     else if (IS_MOVING(x, y))
12160       ContinueMoving(x, y);
12161     else
12162     {
12163       switch (element)
12164       {
12165         case EL_ACID:
12166         case EL_EXIT_OPEN:
12167         case EL_EM_EXIT_OPEN:
12168         case EL_SP_EXIT_OPEN:
12169         case EL_STEEL_EXIT_OPEN:
12170         case EL_EM_STEEL_EXIT_OPEN:
12171         case EL_SP_TERMINAL:
12172         case EL_SP_TERMINAL_ACTIVE:
12173         case EL_EXTRA_TIME:
12174         case EL_SHIELD_NORMAL:
12175         case EL_SHIELD_DEADLY:
12176           if (IS_ANIMATED(graphic))
12177             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12178           break;
12179
12180         case EL_DYNAMITE_ACTIVE:
12181         case EL_EM_DYNAMITE_ACTIVE:
12182         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12183         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12184         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12185         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12186         case EL_SP_DISK_RED_ACTIVE:
12187           CheckDynamite(x, y);
12188           break;
12189
12190         case EL_AMOEBA_GROWING:
12191           AmoebeWaechst(x, y);
12192           break;
12193
12194         case EL_AMOEBA_SHRINKING:
12195           AmoebaDisappearing(x, y);
12196           break;
12197
12198 #if !USE_NEW_AMOEBA_CODE
12199         case EL_AMOEBA_WET:
12200         case EL_AMOEBA_DRY:
12201         case EL_AMOEBA_FULL:
12202         case EL_BD_AMOEBA:
12203         case EL_EMC_DRIPPER:
12204           AmoebeAbleger(x, y);
12205           break;
12206 #endif
12207
12208         case EL_GAME_OF_LIFE:
12209         case EL_BIOMAZE:
12210           Life(x, y);
12211           break;
12212
12213         case EL_EXIT_CLOSED:
12214           CheckExit(x, y);
12215           break;
12216
12217         case EL_EM_EXIT_CLOSED:
12218           CheckExitEM(x, y);
12219           break;
12220
12221         case EL_STEEL_EXIT_CLOSED:
12222           CheckExitSteel(x, y);
12223           break;
12224
12225         case EL_EM_STEEL_EXIT_CLOSED:
12226           CheckExitSteelEM(x, y);
12227           break;
12228
12229         case EL_SP_EXIT_CLOSED:
12230           CheckExitSP(x, y);
12231           break;
12232
12233         case EL_EXPANDABLE_WALL_GROWING:
12234         case EL_EXPANDABLE_STEELWALL_GROWING:
12235           MauerWaechst(x, y);
12236           break;
12237
12238         case EL_EXPANDABLE_WALL:
12239         case EL_EXPANDABLE_WALL_HORIZONTAL:
12240         case EL_EXPANDABLE_WALL_VERTICAL:
12241         case EL_EXPANDABLE_WALL_ANY:
12242         case EL_BD_EXPANDABLE_WALL:
12243           MauerAbleger(x, y);
12244           break;
12245
12246         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12247         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12248         case EL_EXPANDABLE_STEELWALL_ANY:
12249           MauerAblegerStahl(x, y);
12250           break;
12251
12252         case EL_FLAMES:
12253           CheckForDragon(x, y);
12254           break;
12255
12256         case EL_EXPLOSION:
12257           break;
12258
12259         case EL_ELEMENT_SNAPPING:
12260         case EL_DIAGONAL_SHRINKING:
12261         case EL_DIAGONAL_GROWING:
12262         {
12263           graphic =
12264             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12265
12266           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12267           break;
12268         }
12269
12270         default:
12271           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12272             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12273           break;
12274       }
12275     }
12276
12277 #else   // ---------------------------------------------------------------------
12278
12279     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12280     {
12281       StartMoving(x, y);
12282
12283       element = Feld[x][y];
12284       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12285
12286       if (IS_ANIMATED(graphic) &&
12287           !IS_MOVING(x, y) &&
12288           !Stop[x][y])
12289         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12290
12291       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12292         TEST_DrawTwinkleOnField(x, y);
12293     }
12294     else if ((element == EL_ACID ||
12295               element == EL_EXIT_OPEN ||
12296               element == EL_EM_EXIT_OPEN ||
12297               element == EL_SP_EXIT_OPEN ||
12298               element == EL_STEEL_EXIT_OPEN ||
12299               element == EL_EM_STEEL_EXIT_OPEN ||
12300               element == EL_SP_TERMINAL ||
12301               element == EL_SP_TERMINAL_ACTIVE ||
12302               element == EL_EXTRA_TIME ||
12303               element == EL_SHIELD_NORMAL ||
12304               element == EL_SHIELD_DEADLY) &&
12305              IS_ANIMATED(graphic))
12306       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12307     else if (IS_MOVING(x, y))
12308       ContinueMoving(x, y);
12309     else if (IS_ACTIVE_BOMB(element))
12310       CheckDynamite(x, y);
12311     else if (element == EL_AMOEBA_GROWING)
12312       AmoebeWaechst(x, y);
12313     else if (element == EL_AMOEBA_SHRINKING)
12314       AmoebaDisappearing(x, y);
12315
12316 #if !USE_NEW_AMOEBA_CODE
12317     else if (IS_AMOEBALIVE(element))
12318       AmoebeAbleger(x, y);
12319 #endif
12320
12321     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12322       Life(x, y);
12323     else if (element == EL_EXIT_CLOSED)
12324       CheckExit(x, y);
12325     else if (element == EL_EM_EXIT_CLOSED)
12326       CheckExitEM(x, y);
12327     else if (element == EL_STEEL_EXIT_CLOSED)
12328       CheckExitSteel(x, y);
12329     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12330       CheckExitSteelEM(x, y);
12331     else if (element == EL_SP_EXIT_CLOSED)
12332       CheckExitSP(x, y);
12333     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12334              element == EL_EXPANDABLE_STEELWALL_GROWING)
12335       MauerWaechst(x, y);
12336     else if (element == EL_EXPANDABLE_WALL ||
12337              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12338              element == EL_EXPANDABLE_WALL_VERTICAL ||
12339              element == EL_EXPANDABLE_WALL_ANY ||
12340              element == EL_BD_EXPANDABLE_WALL)
12341       MauerAbleger(x, y);
12342     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12343              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12344              element == EL_EXPANDABLE_STEELWALL_ANY)
12345       MauerAblegerStahl(x, y);
12346     else if (element == EL_FLAMES)
12347       CheckForDragon(x, y);
12348     else if (element == EL_EXPLOSION)
12349       ; /* drawing of correct explosion animation is handled separately */
12350     else if (element == EL_ELEMENT_SNAPPING ||
12351              element == EL_DIAGONAL_SHRINKING ||
12352              element == EL_DIAGONAL_GROWING)
12353     {
12354       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12355
12356       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12357     }
12358     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12360
12361 #endif  // ---------------------------------------------------------------------
12362
12363     if (IS_BELT_ACTIVE(element))
12364       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12365
12366     if (game.magic_wall_active)
12367     {
12368       int jx = local_player->jx, jy = local_player->jy;
12369
12370       /* play the element sound at the position nearest to the player */
12371       if ((element == EL_MAGIC_WALL_FULL ||
12372            element == EL_MAGIC_WALL_ACTIVE ||
12373            element == EL_MAGIC_WALL_EMPTYING ||
12374            element == EL_BD_MAGIC_WALL_FULL ||
12375            element == EL_BD_MAGIC_WALL_ACTIVE ||
12376            element == EL_BD_MAGIC_WALL_EMPTYING ||
12377            element == EL_DC_MAGIC_WALL_FULL ||
12378            element == EL_DC_MAGIC_WALL_ACTIVE ||
12379            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12380           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12381       {
12382         magic_wall_x = x;
12383         magic_wall_y = y;
12384       }
12385     }
12386   }
12387
12388 #if 0
12389   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12390 #endif
12391
12392 #if USE_NEW_AMOEBA_CODE
12393   /* new experimental amoeba growth stuff */
12394   if (!(FrameCounter % 8))
12395   {
12396     static unsigned long random = 1684108901;
12397
12398     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12399     {
12400       x = RND(lev_fieldx);
12401       y = RND(lev_fieldy);
12402       element = Feld[x][y];
12403
12404       if (!IS_PLAYER(x,y) &&
12405           (element == EL_EMPTY ||
12406            CAN_GROW_INTO(element) ||
12407            element == EL_QUICKSAND_EMPTY ||
12408            element == EL_QUICKSAND_FAST_EMPTY ||
12409            element == EL_ACID_SPLASH_LEFT ||
12410            element == EL_ACID_SPLASH_RIGHT))
12411       {
12412         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12413             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12414             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12415             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12416           Feld[x][y] = EL_AMOEBA_DROP;
12417       }
12418
12419       random = random * 129 + 1;
12420     }
12421   }
12422 #endif
12423
12424 #if 0
12425   if (game.explosions_delayed)
12426 #endif
12427   {
12428     game.explosions_delayed = FALSE;
12429
12430     SCAN_PLAYFIELD(x, y)
12431     {
12432       element = Feld[x][y];
12433
12434       if (ExplodeField[x][y])
12435         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12436       else if (element == EL_EXPLOSION)
12437         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12438
12439       ExplodeField[x][y] = EX_TYPE_NONE;
12440     }
12441
12442     game.explosions_delayed = TRUE;
12443   }
12444
12445   if (game.magic_wall_active)
12446   {
12447     if (!(game.magic_wall_time_left % 4))
12448     {
12449       int element = Feld[magic_wall_x][magic_wall_y];
12450
12451       if (element == EL_BD_MAGIC_WALL_FULL ||
12452           element == EL_BD_MAGIC_WALL_ACTIVE ||
12453           element == EL_BD_MAGIC_WALL_EMPTYING)
12454         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12455       else if (element == EL_DC_MAGIC_WALL_FULL ||
12456                element == EL_DC_MAGIC_WALL_ACTIVE ||
12457                element == EL_DC_MAGIC_WALL_EMPTYING)
12458         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12459       else
12460         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12461     }
12462
12463     if (game.magic_wall_time_left > 0)
12464     {
12465       game.magic_wall_time_left--;
12466
12467       if (!game.magic_wall_time_left)
12468       {
12469         SCAN_PLAYFIELD(x, y)
12470         {
12471           element = Feld[x][y];
12472
12473           if (element == EL_MAGIC_WALL_ACTIVE ||
12474               element == EL_MAGIC_WALL_FULL)
12475           {
12476             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12477             TEST_DrawLevelField(x, y);
12478           }
12479           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12480                    element == EL_BD_MAGIC_WALL_FULL)
12481           {
12482             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12483             TEST_DrawLevelField(x, y);
12484           }
12485           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12486                    element == EL_DC_MAGIC_WALL_FULL)
12487           {
12488             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12489             TEST_DrawLevelField(x, y);
12490           }
12491         }
12492
12493         game.magic_wall_active = FALSE;
12494       }
12495     }
12496   }
12497
12498   if (game.light_time_left > 0)
12499   {
12500     game.light_time_left--;
12501
12502     if (game.light_time_left == 0)
12503       RedrawAllLightSwitchesAndInvisibleElements();
12504   }
12505
12506   if (game.timegate_time_left > 0)
12507   {
12508     game.timegate_time_left--;
12509
12510     if (game.timegate_time_left == 0)
12511       CloseAllOpenTimegates();
12512   }
12513
12514   if (game.lenses_time_left > 0)
12515   {
12516     game.lenses_time_left--;
12517
12518     if (game.lenses_time_left == 0)
12519       RedrawAllInvisibleElementsForLenses();
12520   }
12521
12522   if (game.magnify_time_left > 0)
12523   {
12524     game.magnify_time_left--;
12525
12526     if (game.magnify_time_left == 0)
12527       RedrawAllInvisibleElementsForMagnifier();
12528   }
12529
12530   for (i = 0; i < MAX_PLAYERS; i++)
12531   {
12532     struct PlayerInfo *player = &stored_player[i];
12533
12534     if (SHIELD_ON(player))
12535     {
12536       if (player->shield_deadly_time_left)
12537         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12538       else if (player->shield_normal_time_left)
12539         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12540     }
12541   }
12542
12543 #if USE_DELAYED_GFX_REDRAW
12544   SCAN_PLAYFIELD(x, y)
12545   {
12546 #if 1
12547     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12548 #else
12549     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12550         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12551 #endif
12552     {
12553       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12554         DrawLevelField(x, y);
12555
12556       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12557         DrawLevelFieldCrumbledSand(x, y);
12558
12559       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12560         DrawLevelFieldCrumbledSandNeighbours(x, y);
12561
12562       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12563         DrawTwinkleOnField(x, y);
12564     }
12565
12566     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12567   }
12568 #endif
12569
12570   CheckLevelTime();
12571
12572   DrawAllPlayers();
12573   PlayAllPlayersSound();
12574
12575   if (options.debug)                    /* calculate frames per second */
12576   {
12577     static unsigned long fps_counter = 0;
12578     static int fps_frames = 0;
12579     unsigned long fps_delay_ms = Counter() - fps_counter;
12580
12581     fps_frames++;
12582
12583     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12584     {
12585       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12586
12587       fps_frames = 0;
12588       fps_counter = Counter();
12589     }
12590
12591     redraw_mask |= REDRAW_FPS;
12592   }
12593
12594   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12595
12596   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12597   {
12598     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12599
12600     local_player->show_envelope = 0;
12601   }
12602
12603 #if 0
12604   debug_print_timestamp(0, "stop main loop profiling ");
12605   printf("----------------------------------------------------------\n");
12606 #endif
12607
12608   /* use random number generator in every frame to make it less predictable */
12609   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12610     RND(1);
12611 }
12612
12613 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12614 {
12615   int min_x = x, min_y = y, max_x = x, max_y = y;
12616   int i;
12617
12618   for (i = 0; i < MAX_PLAYERS; i++)
12619   {
12620     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12621
12622     if (!stored_player[i].active || &stored_player[i] == player)
12623       continue;
12624
12625     min_x = MIN(min_x, jx);
12626     min_y = MIN(min_y, jy);
12627     max_x = MAX(max_x, jx);
12628     max_y = MAX(max_y, jy);
12629   }
12630
12631   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12632 }
12633
12634 static boolean AllPlayersInVisibleScreen()
12635 {
12636   int i;
12637
12638   for (i = 0; i < MAX_PLAYERS; i++)
12639   {
12640     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12641
12642     if (!stored_player[i].active)
12643       continue;
12644
12645     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12646       return FALSE;
12647   }
12648
12649   return TRUE;
12650 }
12651
12652 void ScrollLevel(int dx, int dy)
12653 {
12654 #if 1
12655   static Bitmap *bitmap_db_field2 = NULL;
12656   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12657   int x, y;
12658 #else
12659   int i, x, y;
12660 #endif
12661
12662 #if 0
12663   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12664   /* only horizontal XOR vertical scroll direction allowed */
12665   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12666     return;
12667 #endif
12668
12669 #if 1
12670   if (bitmap_db_field2 == NULL)
12671     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12672
12673   /* needed when blitting directly to same bitmap -- should not be needed with
12674      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12675   BlitBitmap(drawto_field, bitmap_db_field2,
12676              FX + TILEX * (dx == -1) - softscroll_offset,
12677              FY + TILEY * (dy == -1) - softscroll_offset,
12678              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12679              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12680              FX + TILEX * (dx == 1) - softscroll_offset,
12681              FY + TILEY * (dy == 1) - softscroll_offset);
12682   BlitBitmap(bitmap_db_field2, drawto_field,
12683              FX + TILEX * (dx == 1) - softscroll_offset,
12684              FY + TILEY * (dy == 1) - softscroll_offset,
12685              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12686              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12687              FX + TILEX * (dx == 1) - softscroll_offset,
12688              FY + TILEY * (dy == 1) - softscroll_offset);
12689
12690 #else
12691
12692 #if 0
12693   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12694   int xsize = (BX2 - BX1 + 1);
12695   int ysize = (BY2 - BY1 + 1);
12696   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12697   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12698   int step  = (start < end ? +1 : -1);
12699
12700   for (i = start; i != end; i += step)
12701   {
12702     BlitBitmap(drawto_field, drawto_field,
12703                FX + TILEX * (dx != 0 ? i + step : 0),
12704                FY + TILEY * (dy != 0 ? i + step : 0),
12705                TILEX * (dx != 0 ? 1 : xsize),
12706                TILEY * (dy != 0 ? 1 : ysize),
12707                FX + TILEX * (dx != 0 ? i : 0),
12708                FY + TILEY * (dy != 0 ? i : 0));
12709   }
12710
12711 #else
12712
12713   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12714
12715   BlitBitmap(drawto_field, drawto_field,
12716              FX + TILEX * (dx == -1) - softscroll_offset,
12717              FY + TILEY * (dy == -1) - softscroll_offset,
12718              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12719              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12720              FX + TILEX * (dx == 1) - softscroll_offset,
12721              FY + TILEY * (dy == 1) - softscroll_offset);
12722 #endif
12723 #endif
12724
12725   if (dx != 0)
12726   {
12727     x = (dx == 1 ? BX1 : BX2);
12728     for (y = BY1; y <= BY2; y++)
12729       DrawScreenField(x, y);
12730   }
12731
12732   if (dy != 0)
12733   {
12734     y = (dy == 1 ? BY1 : BY2);
12735     for (x = BX1; x <= BX2; x++)
12736       DrawScreenField(x, y);
12737   }
12738
12739   redraw_mask |= REDRAW_FIELD;
12740 }
12741
12742 static boolean canFallDown(struct PlayerInfo *player)
12743 {
12744   int jx = player->jx, jy = player->jy;
12745
12746   return (IN_LEV_FIELD(jx, jy + 1) &&
12747           (IS_FREE(jx, jy + 1) ||
12748            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12749           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12750           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12751 }
12752
12753 static boolean canPassField(int x, int y, int move_dir)
12754 {
12755   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12756   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12757   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12758   int nextx = x + dx;
12759   int nexty = y + dy;
12760   int element = Feld[x][y];
12761
12762   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12763           !CAN_MOVE(element) &&
12764           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12765           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12766           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12767 }
12768
12769 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12770 {
12771   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12772   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12773   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12774   int newx = x + dx;
12775   int newy = y + dy;
12776
12777   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12778           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12779           (IS_DIGGABLE(Feld[newx][newy]) ||
12780            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12781            canPassField(newx, newy, move_dir)));
12782 }
12783
12784 static void CheckGravityMovement(struct PlayerInfo *player)
12785 {
12786 #if USE_PLAYER_GRAVITY
12787   if (player->gravity && !player->programmed_action)
12788 #else
12789   if (game.gravity && !player->programmed_action)
12790 #endif
12791   {
12792     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12793     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12794     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12795     int jx = player->jx, jy = player->jy;
12796     boolean player_is_moving_to_valid_field =
12797       (!player_is_snapping &&
12798        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12799         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12800     boolean player_can_fall_down = canFallDown(player);
12801
12802     if (player_can_fall_down &&
12803         !player_is_moving_to_valid_field)
12804       player->programmed_action = MV_DOWN;
12805   }
12806 }
12807
12808 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12809 {
12810   return CheckGravityMovement(player);
12811
12812 #if USE_PLAYER_GRAVITY
12813   if (player->gravity && !player->programmed_action)
12814 #else
12815   if (game.gravity && !player->programmed_action)
12816 #endif
12817   {
12818     int jx = player->jx, jy = player->jy;
12819     boolean field_under_player_is_free =
12820       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12821     boolean player_is_standing_on_valid_field =
12822       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12823        (IS_WALKABLE(Feld[jx][jy]) &&
12824         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12825
12826     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12827       player->programmed_action = MV_DOWN;
12828   }
12829 }
12830
12831 /*
12832   MovePlayerOneStep()
12833   -----------------------------------------------------------------------------
12834   dx, dy:               direction (non-diagonal) to try to move the player to
12835   real_dx, real_dy:     direction as read from input device (can be diagonal)
12836 */
12837
12838 boolean MovePlayerOneStep(struct PlayerInfo *player,
12839                           int dx, int dy, int real_dx, int real_dy)
12840 {
12841   int jx = player->jx, jy = player->jy;
12842   int new_jx = jx + dx, new_jy = jy + dy;
12843 #if !USE_FIXED_DONT_RUN_INTO
12844   int element;
12845 #endif
12846   int can_move;
12847   boolean player_can_move = !player->cannot_move;
12848
12849   if (!player->active || (!dx && !dy))
12850     return MP_NO_ACTION;
12851
12852   player->MovDir = (dx < 0 ? MV_LEFT :
12853                     dx > 0 ? MV_RIGHT :
12854                     dy < 0 ? MV_UP :
12855                     dy > 0 ? MV_DOWN :  MV_NONE);
12856
12857   if (!IN_LEV_FIELD(new_jx, new_jy))
12858     return MP_NO_ACTION;
12859
12860   if (!player_can_move)
12861   {
12862     if (player->MovPos == 0)
12863     {
12864       player->is_moving = FALSE;
12865       player->is_digging = FALSE;
12866       player->is_collecting = FALSE;
12867       player->is_snapping = FALSE;
12868       player->is_pushing = FALSE;
12869     }
12870   }
12871
12872 #if 1
12873   if (!options.network && game.centered_player_nr == -1 &&
12874       !AllPlayersInSight(player, new_jx, new_jy))
12875     return MP_NO_ACTION;
12876 #else
12877   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12878     return MP_NO_ACTION;
12879 #endif
12880
12881 #if !USE_FIXED_DONT_RUN_INTO
12882   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12883
12884   /* (moved to DigField()) */
12885   if (player_can_move && DONT_RUN_INTO(element))
12886   {
12887     if (element == EL_ACID && dx == 0 && dy == 1)
12888     {
12889       SplashAcid(new_jx, new_jy);
12890       Feld[jx][jy] = EL_PLAYER_1;
12891       InitMovingField(jx, jy, MV_DOWN);
12892       Store[jx][jy] = EL_ACID;
12893       ContinueMoving(jx, jy);
12894       BuryPlayer(player);
12895     }
12896     else
12897       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12898
12899     return MP_MOVING;
12900   }
12901 #endif
12902
12903   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12904   if (can_move != MP_MOVING)
12905     return can_move;
12906
12907   /* check if DigField() has caused relocation of the player */
12908   if (player->jx != jx || player->jy != jy)
12909     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12910
12911   StorePlayer[jx][jy] = 0;
12912   player->last_jx = jx;
12913   player->last_jy = jy;
12914   player->jx = new_jx;
12915   player->jy = new_jy;
12916   StorePlayer[new_jx][new_jy] = player->element_nr;
12917
12918   if (player->move_delay_value_next != -1)
12919   {
12920     player->move_delay_value = player->move_delay_value_next;
12921     player->move_delay_value_next = -1;
12922   }
12923
12924   player->MovPos =
12925     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12926
12927   player->step_counter++;
12928
12929   PlayerVisit[jx][jy] = FrameCounter;
12930
12931 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12932   player->is_moving = TRUE;
12933 #endif
12934
12935 #if 1
12936   /* should better be called in MovePlayer(), but this breaks some tapes */
12937   ScrollPlayer(player, SCROLL_INIT);
12938 #endif
12939
12940   return MP_MOVING;
12941 }
12942
12943 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12944 {
12945   int jx = player->jx, jy = player->jy;
12946   int old_jx = jx, old_jy = jy;
12947   int moved = MP_NO_ACTION;
12948
12949   if (!player->active)
12950     return FALSE;
12951
12952   if (!dx && !dy)
12953   {
12954     if (player->MovPos == 0)
12955     {
12956       player->is_moving = FALSE;
12957       player->is_digging = FALSE;
12958       player->is_collecting = FALSE;
12959       player->is_snapping = FALSE;
12960       player->is_pushing = FALSE;
12961     }
12962
12963     return FALSE;
12964   }
12965
12966   if (player->move_delay > 0)
12967     return FALSE;
12968
12969   player->move_delay = -1;              /* set to "uninitialized" value */
12970
12971   /* store if player is automatically moved to next field */
12972   player->is_auto_moving = (player->programmed_action != MV_NONE);
12973
12974   /* remove the last programmed player action */
12975   player->programmed_action = 0;
12976
12977   if (player->MovPos)
12978   {
12979     /* should only happen if pre-1.2 tape recordings are played */
12980     /* this is only for backward compatibility */
12981
12982     int original_move_delay_value = player->move_delay_value;
12983
12984 #if DEBUG
12985     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12986            tape.counter);
12987 #endif
12988
12989     /* scroll remaining steps with finest movement resolution */
12990     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12991
12992     while (player->MovPos)
12993     {
12994       ScrollPlayer(player, SCROLL_GO_ON);
12995       ScrollScreen(NULL, SCROLL_GO_ON);
12996
12997       AdvanceFrameAndPlayerCounters(player->index_nr);
12998
12999       DrawAllPlayers();
13000       BackToFront();
13001     }
13002
13003     player->move_delay_value = original_move_delay_value;
13004   }
13005
13006   player->is_active = FALSE;
13007
13008   if (player->last_move_dir & MV_HORIZONTAL)
13009   {
13010     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13011       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13012   }
13013   else
13014   {
13015     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13016       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13017   }
13018
13019 #if USE_FIXED_BORDER_RUNNING_GFX
13020   if (!moved && !player->is_active)
13021   {
13022     player->is_moving = FALSE;
13023     player->is_digging = FALSE;
13024     player->is_collecting = FALSE;
13025     player->is_snapping = FALSE;
13026     player->is_pushing = FALSE;
13027   }
13028 #endif
13029
13030   jx = player->jx;
13031   jy = player->jy;
13032
13033 #if 1
13034   if (moved & MP_MOVING && !ScreenMovPos &&
13035       (player->index_nr == game.centered_player_nr ||
13036        game.centered_player_nr == -1))
13037 #else
13038   if (moved & MP_MOVING && !ScreenMovPos &&
13039       (player == local_player || !options.network))
13040 #endif
13041   {
13042     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13043     int offset = game.scroll_delay_value;
13044
13045     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13046     {
13047       /* actual player has left the screen -- scroll in that direction */
13048       if (jx != old_jx)         /* player has moved horizontally */
13049         scroll_x += (jx - old_jx);
13050       else                      /* player has moved vertically */
13051         scroll_y += (jy - old_jy);
13052     }
13053     else
13054     {
13055       if (jx != old_jx)         /* player has moved horizontally */
13056       {
13057         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13058             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13059           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13060
13061         /* don't scroll over playfield boundaries */
13062         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13063           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13064
13065         /* don't scroll more than one field at a time */
13066         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13067
13068         /* don't scroll against the player's moving direction */
13069         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13070             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13071           scroll_x = old_scroll_x;
13072       }
13073       else                      /* player has moved vertically */
13074       {
13075         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13076             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13077           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13078
13079         /* don't scroll over playfield boundaries */
13080         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13081           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13082
13083         /* don't scroll more than one field at a time */
13084         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13085
13086         /* don't scroll against the player's moving direction */
13087         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13088             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13089           scroll_y = old_scroll_y;
13090       }
13091     }
13092
13093     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13094     {
13095 #if 1
13096       if (!options.network && game.centered_player_nr == -1 &&
13097           !AllPlayersInVisibleScreen())
13098       {
13099         scroll_x = old_scroll_x;
13100         scroll_y = old_scroll_y;
13101       }
13102       else
13103 #else
13104       if (!options.network && !AllPlayersInVisibleScreen())
13105       {
13106         scroll_x = old_scroll_x;
13107         scroll_y = old_scroll_y;
13108       }
13109       else
13110 #endif
13111       {
13112         ScrollScreen(player, SCROLL_INIT);
13113         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13114       }
13115     }
13116   }
13117
13118   player->StepFrame = 0;
13119
13120   if (moved & MP_MOVING)
13121   {
13122     if (old_jx != jx && old_jy == jy)
13123       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13124     else if (old_jx == jx && old_jy != jy)
13125       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13126
13127     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13128
13129     player->last_move_dir = player->MovDir;
13130     player->is_moving = TRUE;
13131     player->is_snapping = FALSE;
13132     player->is_switching = FALSE;
13133     player->is_dropping = FALSE;
13134     player->is_dropping_pressed = FALSE;
13135     player->drop_pressed_delay = 0;
13136
13137 #if 0
13138     /* should better be called here than above, but this breaks some tapes */
13139     ScrollPlayer(player, SCROLL_INIT);
13140 #endif
13141   }
13142   else
13143   {
13144     CheckGravityMovementWhenNotMoving(player);
13145
13146     player->is_moving = FALSE;
13147
13148     /* at this point, the player is allowed to move, but cannot move right now
13149        (e.g. because of something blocking the way) -- ensure that the player
13150        is also allowed to move in the next frame (in old versions before 3.1.1,
13151        the player was forced to wait again for eight frames before next try) */
13152
13153     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13154       player->move_delay = 0;   /* allow direct movement in the next frame */
13155   }
13156
13157   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13158     player->move_delay = player->move_delay_value;
13159
13160   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13161   {
13162     TestIfPlayerTouchesBadThing(jx, jy);
13163     TestIfPlayerTouchesCustomElement(jx, jy);
13164   }
13165
13166   if (!player->active)
13167     RemovePlayer(player);
13168
13169   return moved;
13170 }
13171
13172 void ScrollPlayer(struct PlayerInfo *player, int mode)
13173 {
13174   int jx = player->jx, jy = player->jy;
13175   int last_jx = player->last_jx, last_jy = player->last_jy;
13176   int move_stepsize = TILEX / player->move_delay_value;
13177
13178 #if USE_NEW_PLAYER_SPEED
13179   if (!player->active)
13180     return;
13181
13182   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13183     return;
13184 #else
13185   if (!player->active || player->MovPos == 0)
13186     return;
13187 #endif
13188
13189   if (mode == SCROLL_INIT)
13190   {
13191     player->actual_frame_counter = FrameCounter;
13192     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13193
13194     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13195         Feld[last_jx][last_jy] == EL_EMPTY)
13196     {
13197       int last_field_block_delay = 0;   /* start with no blocking at all */
13198       int block_delay_adjustment = player->block_delay_adjustment;
13199
13200       /* if player blocks last field, add delay for exactly one move */
13201       if (player->block_last_field)
13202       {
13203         last_field_block_delay += player->move_delay_value;
13204
13205         /* when blocking enabled, prevent moving up despite gravity */
13206 #if USE_PLAYER_GRAVITY
13207         if (player->gravity && player->MovDir == MV_UP)
13208           block_delay_adjustment = -1;
13209 #else
13210         if (game.gravity && player->MovDir == MV_UP)
13211           block_delay_adjustment = -1;
13212 #endif
13213       }
13214
13215       /* add block delay adjustment (also possible when not blocking) */
13216       last_field_block_delay += block_delay_adjustment;
13217
13218       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13219       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13220     }
13221
13222 #if USE_NEW_PLAYER_SPEED
13223     if (player->MovPos != 0)    /* player has not yet reached destination */
13224       return;
13225 #else
13226     return;
13227 #endif
13228   }
13229   else if (!FrameReached(&player->actual_frame_counter, 1))
13230     return;
13231
13232 #if USE_NEW_PLAYER_SPEED
13233   if (player->MovPos != 0)
13234   {
13235     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13236     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13237
13238     /* before DrawPlayer() to draw correct player graphic for this case */
13239     if (player->MovPos == 0)
13240       CheckGravityMovement(player);
13241   }
13242 #else
13243   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13244   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13245
13246   /* before DrawPlayer() to draw correct player graphic for this case */
13247   if (player->MovPos == 0)
13248     CheckGravityMovement(player);
13249 #endif
13250
13251   if (player->MovPos == 0)      /* player reached destination field */
13252   {
13253     if (player->move_delay_reset_counter > 0)
13254     {
13255       player->move_delay_reset_counter--;
13256
13257       if (player->move_delay_reset_counter == 0)
13258       {
13259         /* continue with normal speed after quickly moving through gate */
13260         HALVE_PLAYER_SPEED(player);
13261
13262         /* be able to make the next move without delay */
13263         player->move_delay = 0;
13264       }
13265     }
13266
13267     player->last_jx = jx;
13268     player->last_jy = jy;
13269
13270     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13271         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13272         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13273         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13274         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13275         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13276     {
13277       DrawPlayer(player);       /* needed here only to cleanup last field */
13278       RemovePlayer(player);
13279
13280       if (local_player->friends_still_needed == 0 ||
13281           IS_SP_ELEMENT(Feld[jx][jy]))
13282         PlayerWins(player);
13283     }
13284
13285     /* this breaks one level: "machine", level 000 */
13286     {
13287       int move_direction = player->MovDir;
13288       int enter_side = MV_DIR_OPPOSITE(move_direction);
13289       int leave_side = move_direction;
13290       int old_jx = last_jx;
13291       int old_jy = last_jy;
13292       int old_element = Feld[old_jx][old_jy];
13293       int new_element = Feld[jx][jy];
13294
13295       if (IS_CUSTOM_ELEMENT(old_element))
13296         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13297                                    CE_LEFT_BY_PLAYER,
13298                                    player->index_bit, leave_side);
13299
13300       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13301                                           CE_PLAYER_LEAVES_X,
13302                                           player->index_bit, leave_side);
13303
13304       if (IS_CUSTOM_ELEMENT(new_element))
13305         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13306                                    player->index_bit, enter_side);
13307
13308       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13309                                           CE_PLAYER_ENTERS_X,
13310                                           player->index_bit, enter_side);
13311
13312       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13313                                         CE_MOVE_OF_X, move_direction);
13314     }
13315
13316     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13317     {
13318       TestIfPlayerTouchesBadThing(jx, jy);
13319       TestIfPlayerTouchesCustomElement(jx, jy);
13320
13321       /* needed because pushed element has not yet reached its destination,
13322          so it would trigger a change event at its previous field location */
13323       if (!player->is_pushing)
13324         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13325
13326       if (!player->active)
13327         RemovePlayer(player);
13328     }
13329
13330     if (!local_player->LevelSolved && level.use_step_counter)
13331     {
13332       int i;
13333
13334       TimePlayed++;
13335
13336       if (TimeLeft > 0)
13337       {
13338         TimeLeft--;
13339
13340         if (TimeLeft <= 10 && setup.time_limit)
13341           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13342
13343 #if 1
13344         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13345
13346         DisplayGameControlValues();
13347 #else
13348         DrawGameValue_Time(TimeLeft);
13349 #endif
13350
13351         if (!TimeLeft && setup.time_limit)
13352           for (i = 0; i < MAX_PLAYERS; i++)
13353             KillPlayer(&stored_player[i]);
13354       }
13355 #if 1
13356       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13357       {
13358         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13359
13360         DisplayGameControlValues();
13361       }
13362 #else
13363       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13364         DrawGameValue_Time(TimePlayed);
13365 #endif
13366     }
13367
13368     if (tape.single_step && tape.recording && !tape.pausing &&
13369         !player->programmed_action)
13370       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13371   }
13372 }
13373
13374 void ScrollScreen(struct PlayerInfo *player, int mode)
13375 {
13376   static unsigned long screen_frame_counter = 0;
13377
13378   if (mode == SCROLL_INIT)
13379   {
13380     /* set scrolling step size according to actual player's moving speed */
13381     ScrollStepSize = TILEX / player->move_delay_value;
13382
13383     screen_frame_counter = FrameCounter;
13384     ScreenMovDir = player->MovDir;
13385     ScreenMovPos = player->MovPos;
13386     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13387     return;
13388   }
13389   else if (!FrameReached(&screen_frame_counter, 1))
13390     return;
13391
13392   if (ScreenMovPos)
13393   {
13394     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13395     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13396     redraw_mask |= REDRAW_FIELD;
13397   }
13398   else
13399     ScreenMovDir = MV_NONE;
13400 }
13401
13402 void TestIfPlayerTouchesCustomElement(int x, int y)
13403 {
13404   static int xy[4][2] =
13405   {
13406     { 0, -1 },
13407     { -1, 0 },
13408     { +1, 0 },
13409     { 0, +1 }
13410   };
13411   static int trigger_sides[4][2] =
13412   {
13413     /* center side       border side */
13414     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13415     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13416     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13417     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13418   };
13419   static int touch_dir[4] =
13420   {
13421     MV_LEFT | MV_RIGHT,
13422     MV_UP   | MV_DOWN,
13423     MV_UP   | MV_DOWN,
13424     MV_LEFT | MV_RIGHT
13425   };
13426   int center_element = Feld[x][y];      /* should always be non-moving! */
13427   int i;
13428
13429   for (i = 0; i < NUM_DIRECTIONS; i++)
13430   {
13431     int xx = x + xy[i][0];
13432     int yy = y + xy[i][1];
13433     int center_side = trigger_sides[i][0];
13434     int border_side = trigger_sides[i][1];
13435     int border_element;
13436
13437     if (!IN_LEV_FIELD(xx, yy))
13438       continue;
13439
13440     if (IS_PLAYER(x, y))
13441     {
13442       struct PlayerInfo *player = PLAYERINFO(x, y);
13443
13444       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13445         border_element = Feld[xx][yy];          /* may be moving! */
13446       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13447         border_element = Feld[xx][yy];
13448       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13449         border_element = MovingOrBlocked2Element(xx, yy);
13450       else
13451         continue;               /* center and border element do not touch */
13452
13453       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13454                                  player->index_bit, border_side);
13455       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13456                                           CE_PLAYER_TOUCHES_X,
13457                                           player->index_bit, border_side);
13458     }
13459     else if (IS_PLAYER(xx, yy))
13460     {
13461       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13462
13463       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13464       {
13465         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13466           continue;             /* center and border element do not touch */
13467       }
13468
13469       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13470                                  player->index_bit, center_side);
13471       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13472                                           CE_PLAYER_TOUCHES_X,
13473                                           player->index_bit, center_side);
13474       break;
13475     }
13476   }
13477 }
13478
13479 #if USE_ELEMENT_TOUCHING_BUGFIX
13480
13481 void TestIfElementTouchesCustomElement(int x, int y)
13482 {
13483   static int xy[4][2] =
13484   {
13485     { 0, -1 },
13486     { -1, 0 },
13487     { +1, 0 },
13488     { 0, +1 }
13489   };
13490   static int trigger_sides[4][2] =
13491   {
13492     /* center side      border side */
13493     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13494     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13495     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13496     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13497   };
13498   static int touch_dir[4] =
13499   {
13500     MV_LEFT | MV_RIGHT,
13501     MV_UP   | MV_DOWN,
13502     MV_UP   | MV_DOWN,
13503     MV_LEFT | MV_RIGHT
13504   };
13505   boolean change_center_element = FALSE;
13506   int center_element = Feld[x][y];      /* should always be non-moving! */
13507   int border_element_old[NUM_DIRECTIONS];
13508   int i;
13509
13510   for (i = 0; i < NUM_DIRECTIONS; i++)
13511   {
13512     int xx = x + xy[i][0];
13513     int yy = y + xy[i][1];
13514     int border_element;
13515
13516     border_element_old[i] = -1;
13517
13518     if (!IN_LEV_FIELD(xx, yy))
13519       continue;
13520
13521     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13522       border_element = Feld[xx][yy];    /* may be moving! */
13523     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13524       border_element = Feld[xx][yy];
13525     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13526       border_element = MovingOrBlocked2Element(xx, yy);
13527     else
13528       continue;                 /* center and border element do not touch */
13529
13530     border_element_old[i] = border_element;
13531   }
13532
13533   for (i = 0; i < NUM_DIRECTIONS; i++)
13534   {
13535     int xx = x + xy[i][0];
13536     int yy = y + xy[i][1];
13537     int center_side = trigger_sides[i][0];
13538     int border_element = border_element_old[i];
13539
13540     if (border_element == -1)
13541       continue;
13542
13543     /* check for change of border element */
13544     CheckElementChangeBySide(xx, yy, border_element, center_element,
13545                              CE_TOUCHING_X, center_side);
13546   }
13547
13548   for (i = 0; i < NUM_DIRECTIONS; i++)
13549   {
13550     int border_side = trigger_sides[i][1];
13551     int border_element = border_element_old[i];
13552
13553     if (border_element == -1)
13554       continue;
13555
13556     /* check for change of center element (but change it only once) */
13557     if (!change_center_element)
13558       change_center_element =
13559         CheckElementChangeBySide(x, y, center_element, border_element,
13560                                  CE_TOUCHING_X, border_side);
13561   }
13562 }
13563
13564 #else
13565
13566 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13567 {
13568   static int xy[4][2] =
13569   {
13570     { 0, -1 },
13571     { -1, 0 },
13572     { +1, 0 },
13573     { 0, +1 }
13574   };
13575   static int trigger_sides[4][2] =
13576   {
13577     /* center side      border side */
13578     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13579     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13580     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13581     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13582   };
13583   static int touch_dir[4] =
13584   {
13585     MV_LEFT | MV_RIGHT,
13586     MV_UP   | MV_DOWN,
13587     MV_UP   | MV_DOWN,
13588     MV_LEFT | MV_RIGHT
13589   };
13590   boolean change_center_element = FALSE;
13591   int center_element = Feld[x][y];      /* should always be non-moving! */
13592   int i;
13593
13594   for (i = 0; i < NUM_DIRECTIONS; i++)
13595   {
13596     int xx = x + xy[i][0];
13597     int yy = y + xy[i][1];
13598     int center_side = trigger_sides[i][0];
13599     int border_side = trigger_sides[i][1];
13600     int border_element;
13601
13602     if (!IN_LEV_FIELD(xx, yy))
13603       continue;
13604
13605     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13606       border_element = Feld[xx][yy];    /* may be moving! */
13607     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13608       border_element = Feld[xx][yy];
13609     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13610       border_element = MovingOrBlocked2Element(xx, yy);
13611     else
13612       continue;                 /* center and border element do not touch */
13613
13614     /* check for change of center element (but change it only once) */
13615     if (!change_center_element)
13616       change_center_element =
13617         CheckElementChangeBySide(x, y, center_element, border_element,
13618                                  CE_TOUCHING_X, border_side);
13619
13620     /* check for change of border element */
13621     CheckElementChangeBySide(xx, yy, border_element, center_element,
13622                              CE_TOUCHING_X, center_side);
13623   }
13624 }
13625
13626 #endif
13627
13628 void TestIfElementHitsCustomElement(int x, int y, int direction)
13629 {
13630   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13631   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13632   int hitx = x + dx, hity = y + dy;
13633   int hitting_element = Feld[x][y];
13634   int touched_element;
13635
13636   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13637     return;
13638
13639   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13640                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13641
13642   if (IN_LEV_FIELD(hitx, hity))
13643   {
13644     int opposite_direction = MV_DIR_OPPOSITE(direction);
13645     int hitting_side = direction;
13646     int touched_side = opposite_direction;
13647     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13648                           MovDir[hitx][hity] != direction ||
13649                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13650
13651     object_hit = TRUE;
13652
13653     if (object_hit)
13654     {
13655       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13656                                CE_HITTING_X, touched_side);
13657
13658       CheckElementChangeBySide(hitx, hity, touched_element,
13659                                hitting_element, CE_HIT_BY_X, hitting_side);
13660
13661       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13662                                CE_HIT_BY_SOMETHING, opposite_direction);
13663     }
13664   }
13665
13666   /* "hitting something" is also true when hitting the playfield border */
13667   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13668                            CE_HITTING_SOMETHING, direction);
13669 }
13670
13671 #if 0
13672 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13673 {
13674   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13675   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13676   int hitx = x + dx, hity = y + dy;
13677   int hitting_element = Feld[x][y];
13678   int touched_element;
13679 #if 0
13680   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13681                         !IS_FREE(hitx, hity) &&
13682                         (!IS_MOVING(hitx, hity) ||
13683                          MovDir[hitx][hity] != direction ||
13684                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13685 #endif
13686
13687   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13688     return;
13689
13690 #if 0
13691   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13692     return;
13693 #endif
13694
13695   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13696                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13697
13698   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13699                            EP_CAN_SMASH_EVERYTHING, direction);
13700
13701   if (IN_LEV_FIELD(hitx, hity))
13702   {
13703     int opposite_direction = MV_DIR_OPPOSITE(direction);
13704     int hitting_side = direction;
13705     int touched_side = opposite_direction;
13706 #if 0
13707     int touched_element = MovingOrBlocked2Element(hitx, hity);
13708 #endif
13709 #if 1
13710     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13711                           MovDir[hitx][hity] != direction ||
13712                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13713
13714     object_hit = TRUE;
13715 #endif
13716
13717     if (object_hit)
13718     {
13719       int i;
13720
13721       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13722                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13723
13724       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13725                                CE_OTHER_IS_SMASHING, touched_side);
13726
13727       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13728                                CE_OTHER_GETS_SMASHED, hitting_side);
13729     }
13730   }
13731 }
13732 #endif
13733
13734 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13735 {
13736   int i, kill_x = -1, kill_y = -1;
13737
13738   int bad_element = -1;
13739   static int test_xy[4][2] =
13740   {
13741     { 0, -1 },
13742     { -1, 0 },
13743     { +1, 0 },
13744     { 0, +1 }
13745   };
13746   static int test_dir[4] =
13747   {
13748     MV_UP,
13749     MV_LEFT,
13750     MV_RIGHT,
13751     MV_DOWN
13752   };
13753
13754   for (i = 0; i < NUM_DIRECTIONS; i++)
13755   {
13756     int test_x, test_y, test_move_dir, test_element;
13757
13758     test_x = good_x + test_xy[i][0];
13759     test_y = good_y + test_xy[i][1];
13760
13761     if (!IN_LEV_FIELD(test_x, test_y))
13762       continue;
13763
13764     test_move_dir =
13765       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13766
13767     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13768
13769     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13770        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13771     */
13772     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13773         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13774     {
13775       kill_x = test_x;
13776       kill_y = test_y;
13777       bad_element = test_element;
13778
13779       break;
13780     }
13781   }
13782
13783   if (kill_x != -1 || kill_y != -1)
13784   {
13785     if (IS_PLAYER(good_x, good_y))
13786     {
13787       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13788
13789       if (player->shield_deadly_time_left > 0 &&
13790           !IS_INDESTRUCTIBLE(bad_element))
13791         Bang(kill_x, kill_y);
13792       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13793         KillPlayer(player);
13794     }
13795     else
13796       Bang(good_x, good_y);
13797   }
13798 }
13799
13800 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13801 {
13802   int i, kill_x = -1, kill_y = -1;
13803   int bad_element = Feld[bad_x][bad_y];
13804   static int test_xy[4][2] =
13805   {
13806     { 0, -1 },
13807     { -1, 0 },
13808     { +1, 0 },
13809     { 0, +1 }
13810   };
13811   static int touch_dir[4] =
13812   {
13813     MV_LEFT | MV_RIGHT,
13814     MV_UP   | MV_DOWN,
13815     MV_UP   | MV_DOWN,
13816     MV_LEFT | MV_RIGHT
13817   };
13818   static int test_dir[4] =
13819   {
13820     MV_UP,
13821     MV_LEFT,
13822     MV_RIGHT,
13823     MV_DOWN
13824   };
13825
13826   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13827     return;
13828
13829   for (i = 0; i < NUM_DIRECTIONS; i++)
13830   {
13831     int test_x, test_y, test_move_dir, test_element;
13832
13833     test_x = bad_x + test_xy[i][0];
13834     test_y = bad_y + test_xy[i][1];
13835     if (!IN_LEV_FIELD(test_x, test_y))
13836       continue;
13837
13838     test_move_dir =
13839       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13840
13841     test_element = Feld[test_x][test_y];
13842
13843     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13844        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13845     */
13846     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13847         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13848     {
13849       /* good thing is player or penguin that does not move away */
13850       if (IS_PLAYER(test_x, test_y))
13851       {
13852         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13853
13854         if (bad_element == EL_ROBOT && player->is_moving)
13855           continue;     /* robot does not kill player if he is moving */
13856
13857         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13858         {
13859           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13860             continue;           /* center and border element do not touch */
13861         }
13862
13863         kill_x = test_x;
13864         kill_y = test_y;
13865         break;
13866       }
13867       else if (test_element == EL_PENGUIN)
13868       {
13869         kill_x = test_x;
13870         kill_y = test_y;
13871         break;
13872       }
13873     }
13874   }
13875
13876   if (kill_x != -1 || kill_y != -1)
13877   {
13878     if (IS_PLAYER(kill_x, kill_y))
13879     {
13880       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13881
13882       if (player->shield_deadly_time_left > 0 &&
13883           !IS_INDESTRUCTIBLE(bad_element))
13884         Bang(bad_x, bad_y);
13885       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13886         KillPlayer(player);
13887     }
13888     else
13889       Bang(kill_x, kill_y);
13890   }
13891 }
13892
13893 void TestIfPlayerTouchesBadThing(int x, int y)
13894 {
13895   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13896 }
13897
13898 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13899 {
13900   TestIfGoodThingHitsBadThing(x, y, move_dir);
13901 }
13902
13903 void TestIfBadThingTouchesPlayer(int x, int y)
13904 {
13905   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13906 }
13907
13908 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13909 {
13910   TestIfBadThingHitsGoodThing(x, y, move_dir);
13911 }
13912
13913 void TestIfFriendTouchesBadThing(int x, int y)
13914 {
13915   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13916 }
13917
13918 void TestIfBadThingTouchesFriend(int x, int y)
13919 {
13920   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13921 }
13922
13923 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13924 {
13925   int i, kill_x = bad_x, kill_y = bad_y;
13926   static int xy[4][2] =
13927   {
13928     { 0, -1 },
13929     { -1, 0 },
13930     { +1, 0 },
13931     { 0, +1 }
13932   };
13933
13934   for (i = 0; i < NUM_DIRECTIONS; i++)
13935   {
13936     int x, y, element;
13937
13938     x = bad_x + xy[i][0];
13939     y = bad_y + xy[i][1];
13940     if (!IN_LEV_FIELD(x, y))
13941       continue;
13942
13943     element = Feld[x][y];
13944     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13945         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13946     {
13947       kill_x = x;
13948       kill_y = y;
13949       break;
13950     }
13951   }
13952
13953   if (kill_x != bad_x || kill_y != bad_y)
13954     Bang(bad_x, bad_y);
13955 }
13956
13957 void KillPlayer(struct PlayerInfo *player)
13958 {
13959   int jx = player->jx, jy = player->jy;
13960
13961   if (!player->active)
13962     return;
13963
13964   /* the following code was introduced to prevent an infinite loop when calling
13965      -> Bang()
13966      -> CheckTriggeredElementChangeExt()
13967      -> ExecuteCustomElementAction()
13968      -> KillPlayer()
13969      -> (infinitely repeating the above sequence of function calls)
13970      which occurs when killing the player while having a CE with the setting
13971      "kill player X when explosion of <player X>"; the solution using a new
13972      field "player->killed" was chosen for backwards compatibility, although
13973      clever use of the fields "player->active" etc. would probably also work */
13974 #if 1
13975   if (player->killed)
13976     return;
13977 #endif
13978
13979   player->killed = TRUE;
13980
13981   /* remove accessible field at the player's position */
13982   Feld[jx][jy] = EL_EMPTY;
13983
13984   /* deactivate shield (else Bang()/Explode() would not work right) */
13985   player->shield_normal_time_left = 0;
13986   player->shield_deadly_time_left = 0;
13987
13988   Bang(jx, jy);
13989   BuryPlayer(player);
13990 }
13991
13992 static void KillPlayerUnlessEnemyProtected(int x, int y)
13993 {
13994   if (!PLAYER_ENEMY_PROTECTED(x, y))
13995     KillPlayer(PLAYERINFO(x, y));
13996 }
13997
13998 static void KillPlayerUnlessExplosionProtected(int x, int y)
13999 {
14000   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14001     KillPlayer(PLAYERINFO(x, y));
14002 }
14003
14004 void BuryPlayer(struct PlayerInfo *player)
14005 {
14006   int jx = player->jx, jy = player->jy;
14007
14008   if (!player->active)
14009     return;
14010
14011   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14012   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14013
14014   player->GameOver = TRUE;
14015   RemovePlayer(player);
14016 }
14017
14018 void RemovePlayer(struct PlayerInfo *player)
14019 {
14020   int jx = player->jx, jy = player->jy;
14021   int i, found = FALSE;
14022
14023   player->present = FALSE;
14024   player->active = FALSE;
14025
14026   if (!ExplodeField[jx][jy])
14027     StorePlayer[jx][jy] = 0;
14028
14029   if (player->is_moving)
14030     TEST_DrawLevelField(player->last_jx, player->last_jy);
14031
14032   for (i = 0; i < MAX_PLAYERS; i++)
14033     if (stored_player[i].active)
14034       found = TRUE;
14035
14036   if (!found)
14037     AllPlayersGone = TRUE;
14038
14039   ExitX = ZX = jx;
14040   ExitY = ZY = jy;
14041 }
14042
14043 #if USE_NEW_SNAP_DELAY
14044 static void setFieldForSnapping(int x, int y, int element, int direction)
14045 {
14046   struct ElementInfo *ei = &element_info[element];
14047   int direction_bit = MV_DIR_TO_BIT(direction);
14048   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14049   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14050                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14051
14052   Feld[x][y] = EL_ELEMENT_SNAPPING;
14053   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14054
14055   ResetGfxAnimation(x, y);
14056
14057   GfxElement[x][y] = element;
14058   GfxAction[x][y] = action;
14059   GfxDir[x][y] = direction;
14060   GfxFrame[x][y] = -1;
14061 }
14062 #endif
14063
14064 /*
14065   =============================================================================
14066   checkDiagonalPushing()
14067   -----------------------------------------------------------------------------
14068   check if diagonal input device direction results in pushing of object
14069   (by checking if the alternative direction is walkable, diggable, ...)
14070   =============================================================================
14071 */
14072
14073 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14074                                     int x, int y, int real_dx, int real_dy)
14075 {
14076   int jx, jy, dx, dy, xx, yy;
14077
14078   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14079     return TRUE;
14080
14081   /* diagonal direction: check alternative direction */
14082   jx = player->jx;
14083   jy = player->jy;
14084   dx = x - jx;
14085   dy = y - jy;
14086   xx = jx + (dx == 0 ? real_dx : 0);
14087   yy = jy + (dy == 0 ? real_dy : 0);
14088
14089   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14090 }
14091
14092 /*
14093   =============================================================================
14094   DigField()
14095   -----------------------------------------------------------------------------
14096   x, y:                 field next to player (non-diagonal) to try to dig to
14097   real_dx, real_dy:     direction as read from input device (can be diagonal)
14098   =============================================================================
14099 */
14100
14101 static int DigField(struct PlayerInfo *player,
14102                     int oldx, int oldy, int x, int y,
14103                     int real_dx, int real_dy, int mode)
14104 {
14105   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14106   boolean player_was_pushing = player->is_pushing;
14107   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14108   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14109   int jx = oldx, jy = oldy;
14110   int dx = x - jx, dy = y - jy;
14111   int nextx = x + dx, nexty = y + dy;
14112   int move_direction = (dx == -1 ? MV_LEFT  :
14113                         dx == +1 ? MV_RIGHT :
14114                         dy == -1 ? MV_UP    :
14115                         dy == +1 ? MV_DOWN  : MV_NONE);
14116   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14117   int dig_side = MV_DIR_OPPOSITE(move_direction);
14118   int old_element = Feld[jx][jy];
14119 #if USE_FIXED_DONT_RUN_INTO
14120   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14121 #else
14122   int element;
14123 #endif
14124   int collect_count;
14125
14126   if (is_player)                /* function can also be called by EL_PENGUIN */
14127   {
14128     if (player->MovPos == 0)
14129     {
14130       player->is_digging = FALSE;
14131       player->is_collecting = FALSE;
14132     }
14133
14134     if (player->MovPos == 0)    /* last pushing move finished */
14135       player->is_pushing = FALSE;
14136
14137     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14138     {
14139       player->is_switching = FALSE;
14140       player->push_delay = -1;
14141
14142       return MP_NO_ACTION;
14143     }
14144   }
14145
14146 #if !USE_FIXED_DONT_RUN_INTO
14147   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14148     return MP_NO_ACTION;
14149 #endif
14150
14151   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14152     old_element = Back[jx][jy];
14153
14154   /* in case of element dropped at player position, check background */
14155   else if (Back[jx][jy] != EL_EMPTY &&
14156            game.engine_version >= VERSION_IDENT(2,2,0,0))
14157     old_element = Back[jx][jy];
14158
14159   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14160     return MP_NO_ACTION;        /* field has no opening in this direction */
14161
14162   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14163     return MP_NO_ACTION;        /* field has no opening in this direction */
14164
14165 #if USE_FIXED_DONT_RUN_INTO
14166   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14167   {
14168     SplashAcid(x, y);
14169
14170     Feld[jx][jy] = player->artwork_element;
14171     InitMovingField(jx, jy, MV_DOWN);
14172     Store[jx][jy] = EL_ACID;
14173     ContinueMoving(jx, jy);
14174     BuryPlayer(player);
14175
14176     return MP_DONT_RUN_INTO;
14177   }
14178 #endif
14179
14180 #if USE_FIXED_DONT_RUN_INTO
14181   if (player_can_move && DONT_RUN_INTO(element))
14182   {
14183     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14184
14185     return MP_DONT_RUN_INTO;
14186   }
14187 #endif
14188
14189 #if USE_FIXED_DONT_RUN_INTO
14190   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14191     return MP_NO_ACTION;
14192 #endif
14193
14194 #if !USE_FIXED_DONT_RUN_INTO
14195   element = Feld[x][y];
14196 #endif
14197
14198   collect_count = element_info[element].collect_count_initial;
14199
14200   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14201     return MP_NO_ACTION;
14202
14203   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14204     player_can_move = player_can_move_or_snap;
14205
14206   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14207       game.engine_version >= VERSION_IDENT(2,2,0,0))
14208   {
14209     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14210                                player->index_bit, dig_side);
14211     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14212                                         player->index_bit, dig_side);
14213
14214     if (element == EL_DC_LANDMINE)
14215       Bang(x, y);
14216
14217     if (Feld[x][y] != element)          /* field changed by snapping */
14218       return MP_ACTION;
14219
14220     return MP_NO_ACTION;
14221   }
14222
14223 #if USE_PLAYER_GRAVITY
14224   if (player->gravity && is_player && !player->is_auto_moving &&
14225       canFallDown(player) && move_direction != MV_DOWN &&
14226       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14227     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14228 #else
14229   if (game.gravity && is_player && !player->is_auto_moving &&
14230       canFallDown(player) && move_direction != MV_DOWN &&
14231       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14232     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14233 #endif
14234
14235   if (player_can_move &&
14236       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14237   {
14238     int sound_element = SND_ELEMENT(element);
14239     int sound_action = ACTION_WALKING;
14240
14241     if (IS_RND_GATE(element))
14242     {
14243       if (!player->key[RND_GATE_NR(element)])
14244         return MP_NO_ACTION;
14245     }
14246     else if (IS_RND_GATE_GRAY(element))
14247     {
14248       if (!player->key[RND_GATE_GRAY_NR(element)])
14249         return MP_NO_ACTION;
14250     }
14251     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14252     {
14253       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14254         return MP_NO_ACTION;
14255     }
14256     else if (element == EL_EXIT_OPEN ||
14257              element == EL_EM_EXIT_OPEN ||
14258              element == EL_STEEL_EXIT_OPEN ||
14259              element == EL_EM_STEEL_EXIT_OPEN ||
14260              element == EL_SP_EXIT_OPEN ||
14261              element == EL_SP_EXIT_OPENING)
14262     {
14263       sound_action = ACTION_PASSING;    /* player is passing exit */
14264     }
14265     else if (element == EL_EMPTY)
14266     {
14267       sound_action = ACTION_MOVING;             /* nothing to walk on */
14268     }
14269
14270     /* play sound from background or player, whatever is available */
14271     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14272       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14273     else
14274       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14275   }
14276   else if (player_can_move &&
14277            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14278   {
14279     if (!ACCESS_FROM(element, opposite_direction))
14280       return MP_NO_ACTION;      /* field not accessible from this direction */
14281
14282     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14283       return MP_NO_ACTION;
14284
14285     if (IS_EM_GATE(element))
14286     {
14287       if (!player->key[EM_GATE_NR(element)])
14288         return MP_NO_ACTION;
14289     }
14290     else if (IS_EM_GATE_GRAY(element))
14291     {
14292       if (!player->key[EM_GATE_GRAY_NR(element)])
14293         return MP_NO_ACTION;
14294     }
14295     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14296     {
14297       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14298         return MP_NO_ACTION;
14299     }
14300     else if (IS_EMC_GATE(element))
14301     {
14302       if (!player->key[EMC_GATE_NR(element)])
14303         return MP_NO_ACTION;
14304     }
14305     else if (IS_EMC_GATE_GRAY(element))
14306     {
14307       if (!player->key[EMC_GATE_GRAY_NR(element)])
14308         return MP_NO_ACTION;
14309     }
14310     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14311     {
14312       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14313         return MP_NO_ACTION;
14314     }
14315     else if (element == EL_DC_GATE_WHITE ||
14316              element == EL_DC_GATE_WHITE_GRAY ||
14317              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14318     {
14319       if (player->num_white_keys == 0)
14320         return MP_NO_ACTION;
14321
14322       player->num_white_keys--;
14323     }
14324     else if (IS_SP_PORT(element))
14325     {
14326       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14327           element == EL_SP_GRAVITY_PORT_RIGHT ||
14328           element == EL_SP_GRAVITY_PORT_UP ||
14329           element == EL_SP_GRAVITY_PORT_DOWN)
14330 #if USE_PLAYER_GRAVITY
14331         player->gravity = !player->gravity;
14332 #else
14333         game.gravity = !game.gravity;
14334 #endif
14335       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14336                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14337                element == EL_SP_GRAVITY_ON_PORT_UP ||
14338                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14339 #if USE_PLAYER_GRAVITY
14340         player->gravity = TRUE;
14341 #else
14342         game.gravity = TRUE;
14343 #endif
14344       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14345                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14346                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14347                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14348 #if USE_PLAYER_GRAVITY
14349         player->gravity = FALSE;
14350 #else
14351         game.gravity = FALSE;
14352 #endif
14353     }
14354
14355     /* automatically move to the next field with double speed */
14356     player->programmed_action = move_direction;
14357
14358     if (player->move_delay_reset_counter == 0)
14359     {
14360       player->move_delay_reset_counter = 2;     /* two double speed steps */
14361
14362       DOUBLE_PLAYER_SPEED(player);
14363     }
14364
14365     PlayLevelSoundAction(x, y, ACTION_PASSING);
14366   }
14367   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14368   {
14369     RemoveField(x, y);
14370
14371     if (mode != DF_SNAP)
14372     {
14373       GfxElement[x][y] = GFX_ELEMENT(element);
14374       player->is_digging = TRUE;
14375     }
14376
14377     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14378
14379     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14380                                         player->index_bit, dig_side);
14381
14382     if (mode == DF_SNAP)
14383     {
14384 #if USE_NEW_SNAP_DELAY
14385       if (level.block_snap_field)
14386         setFieldForSnapping(x, y, element, move_direction);
14387       else
14388         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14389 #else
14390       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14391 #endif
14392
14393       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14394                                           player->index_bit, dig_side);
14395     }
14396   }
14397   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14398   {
14399     RemoveField(x, y);
14400
14401     if (is_player && mode != DF_SNAP)
14402     {
14403       GfxElement[x][y] = element;
14404       player->is_collecting = TRUE;
14405     }
14406
14407     if (element == EL_SPEED_PILL)
14408     {
14409       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14410     }
14411     else if (element == EL_EXTRA_TIME && level.time > 0)
14412     {
14413       TimeLeft += level.extra_time;
14414
14415 #if 1
14416       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14417
14418       DisplayGameControlValues();
14419 #else
14420       DrawGameValue_Time(TimeLeft);
14421 #endif
14422     }
14423     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14424     {
14425       player->shield_normal_time_left += level.shield_normal_time;
14426       if (element == EL_SHIELD_DEADLY)
14427         player->shield_deadly_time_left += level.shield_deadly_time;
14428     }
14429     else if (element == EL_DYNAMITE ||
14430              element == EL_EM_DYNAMITE ||
14431              element == EL_SP_DISK_RED)
14432     {
14433       if (player->inventory_size < MAX_INVENTORY_SIZE)
14434         player->inventory_element[player->inventory_size++] = element;
14435
14436       DrawGameDoorValues();
14437     }
14438     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14439     {
14440       player->dynabomb_count++;
14441       player->dynabombs_left++;
14442     }
14443     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14444     {
14445       player->dynabomb_size++;
14446     }
14447     else if (element == EL_DYNABOMB_INCREASE_POWER)
14448     {
14449       player->dynabomb_xl = TRUE;
14450     }
14451     else if (IS_KEY(element))
14452     {
14453       player->key[KEY_NR(element)] = TRUE;
14454
14455       DrawGameDoorValues();
14456     }
14457     else if (element == EL_DC_KEY_WHITE)
14458     {
14459       player->num_white_keys++;
14460
14461       /* display white keys? */
14462       /* DrawGameDoorValues(); */
14463     }
14464     else if (IS_ENVELOPE(element))
14465     {
14466       player->show_envelope = element;
14467     }
14468     else if (element == EL_EMC_LENSES)
14469     {
14470       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14471
14472       RedrawAllInvisibleElementsForLenses();
14473     }
14474     else if (element == EL_EMC_MAGNIFIER)
14475     {
14476       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14477
14478       RedrawAllInvisibleElementsForMagnifier();
14479     }
14480     else if (IS_DROPPABLE(element) ||
14481              IS_THROWABLE(element))     /* can be collected and dropped */
14482     {
14483       int i;
14484
14485       if (collect_count == 0)
14486         player->inventory_infinite_element = element;
14487       else
14488         for (i = 0; i < collect_count; i++)
14489           if (player->inventory_size < MAX_INVENTORY_SIZE)
14490             player->inventory_element[player->inventory_size++] = element;
14491
14492       DrawGameDoorValues();
14493     }
14494     else if (collect_count > 0)
14495     {
14496       local_player->gems_still_needed -= collect_count;
14497       if (local_player->gems_still_needed < 0)
14498         local_player->gems_still_needed = 0;
14499
14500 #if 1
14501       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14502
14503       DisplayGameControlValues();
14504 #else
14505       DrawGameValue_Emeralds(local_player->gems_still_needed);
14506 #endif
14507     }
14508
14509     RaiseScoreElement(element);
14510     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14511
14512     if (is_player)
14513       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14514                                           player->index_bit, dig_side);
14515
14516     if (mode == DF_SNAP)
14517     {
14518 #if USE_NEW_SNAP_DELAY
14519       if (level.block_snap_field)
14520         setFieldForSnapping(x, y, element, move_direction);
14521       else
14522         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14523 #else
14524       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14525 #endif
14526
14527       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14528                                           player->index_bit, dig_side);
14529     }
14530   }
14531   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14532   {
14533     if (mode == DF_SNAP && element != EL_BD_ROCK)
14534       return MP_NO_ACTION;
14535
14536     if (CAN_FALL(element) && dy)
14537       return MP_NO_ACTION;
14538
14539     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14540         !(element == EL_SPRING && level.use_spring_bug))
14541       return MP_NO_ACTION;
14542
14543     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14544         ((move_direction & MV_VERTICAL &&
14545           ((element_info[element].move_pattern & MV_LEFT &&
14546             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14547            (element_info[element].move_pattern & MV_RIGHT &&
14548             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14549          (move_direction & MV_HORIZONTAL &&
14550           ((element_info[element].move_pattern & MV_UP &&
14551             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14552            (element_info[element].move_pattern & MV_DOWN &&
14553             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14554       return MP_NO_ACTION;
14555
14556     /* do not push elements already moving away faster than player */
14557     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14558         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14559       return MP_NO_ACTION;
14560
14561     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14562     {
14563       if (player->push_delay_value == -1 || !player_was_pushing)
14564         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14565     }
14566     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14567     {
14568       if (player->push_delay_value == -1)
14569         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14570     }
14571     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14572     {
14573       if (!player->is_pushing)
14574         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14575     }
14576
14577     player->is_pushing = TRUE;
14578     player->is_active = TRUE;
14579
14580     if (!(IN_LEV_FIELD(nextx, nexty) &&
14581           (IS_FREE(nextx, nexty) ||
14582            (IS_SB_ELEMENT(element) &&
14583             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14584            (IS_CUSTOM_ELEMENT(element) &&
14585             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14586       return MP_NO_ACTION;
14587
14588     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14589       return MP_NO_ACTION;
14590
14591     if (player->push_delay == -1)       /* new pushing; restart delay */
14592       player->push_delay = 0;
14593
14594     if (player->push_delay < player->push_delay_value &&
14595         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14596         element != EL_SPRING && element != EL_BALLOON)
14597     {
14598       /* make sure that there is no move delay before next try to push */
14599       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14600         player->move_delay = 0;
14601
14602       return MP_NO_ACTION;
14603     }
14604
14605     if (IS_CUSTOM_ELEMENT(element) &&
14606         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14607     {
14608       if (!DigFieldByCE(nextx, nexty, element))
14609         return MP_NO_ACTION;
14610     }
14611
14612     if (IS_SB_ELEMENT(element))
14613     {
14614       if (element == EL_SOKOBAN_FIELD_FULL)
14615       {
14616         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14617         local_player->sokobanfields_still_needed++;
14618       }
14619
14620       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14621       {
14622         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14623         local_player->sokobanfields_still_needed--;
14624       }
14625
14626       Feld[x][y] = EL_SOKOBAN_OBJECT;
14627
14628       if (Back[x][y] == Back[nextx][nexty])
14629         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14630       else if (Back[x][y] != 0)
14631         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14632                                     ACTION_EMPTYING);
14633       else
14634         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14635                                     ACTION_FILLING);
14636
14637       if (local_player->sokobanfields_still_needed == 0 &&
14638           game.emulation == EMU_SOKOBAN)
14639       {
14640         PlayerWins(player);
14641
14642         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14643       }
14644     }
14645     else
14646       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14647
14648     InitMovingField(x, y, move_direction);
14649     GfxAction[x][y] = ACTION_PUSHING;
14650
14651     if (mode == DF_SNAP)
14652       ContinueMoving(x, y);
14653     else
14654       MovPos[x][y] = (dx != 0 ? dx : dy);
14655
14656     Pushed[x][y] = TRUE;
14657     Pushed[nextx][nexty] = TRUE;
14658
14659     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14660       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14661     else
14662       player->push_delay_value = -1;    /* get new value later */
14663
14664     /* check for element change _after_ element has been pushed */
14665     if (game.use_change_when_pushing_bug)
14666     {
14667       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14668                                  player->index_bit, dig_side);
14669       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14670                                           player->index_bit, dig_side);
14671     }
14672   }
14673   else if (IS_SWITCHABLE(element))
14674   {
14675     if (PLAYER_SWITCHING(player, x, y))
14676     {
14677       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14678                                           player->index_bit, dig_side);
14679
14680       return MP_ACTION;
14681     }
14682
14683     player->is_switching = TRUE;
14684     player->switch_x = x;
14685     player->switch_y = y;
14686
14687     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14688
14689     if (element == EL_ROBOT_WHEEL)
14690     {
14691       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14692       ZX = x;
14693       ZY = y;
14694
14695       game.robot_wheel_active = TRUE;
14696
14697       TEST_DrawLevelField(x, y);
14698     }
14699     else if (element == EL_SP_TERMINAL)
14700     {
14701       int xx, yy;
14702
14703       SCAN_PLAYFIELD(xx, yy)
14704       {
14705         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14706           Bang(xx, yy);
14707         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14708           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14709       }
14710     }
14711     else if (IS_BELT_SWITCH(element))
14712     {
14713       ToggleBeltSwitch(x, y);
14714     }
14715     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14716              element == EL_SWITCHGATE_SWITCH_DOWN ||
14717              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14718              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14719     {
14720       ToggleSwitchgateSwitch(x, y);
14721     }
14722     else if (element == EL_LIGHT_SWITCH ||
14723              element == EL_LIGHT_SWITCH_ACTIVE)
14724     {
14725       ToggleLightSwitch(x, y);
14726     }
14727     else if (element == EL_TIMEGATE_SWITCH ||
14728              element == EL_DC_TIMEGATE_SWITCH)
14729     {
14730       ActivateTimegateSwitch(x, y);
14731     }
14732     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14733              element == EL_BALLOON_SWITCH_RIGHT ||
14734              element == EL_BALLOON_SWITCH_UP    ||
14735              element == EL_BALLOON_SWITCH_DOWN  ||
14736              element == EL_BALLOON_SWITCH_NONE  ||
14737              element == EL_BALLOON_SWITCH_ANY)
14738     {
14739       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14740                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14741                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14742                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14743                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14744                              move_direction);
14745     }
14746     else if (element == EL_LAMP)
14747     {
14748       Feld[x][y] = EL_LAMP_ACTIVE;
14749       local_player->lights_still_needed--;
14750
14751       ResetGfxAnimation(x, y);
14752       TEST_DrawLevelField(x, y);
14753     }
14754     else if (element == EL_TIME_ORB_FULL)
14755     {
14756       Feld[x][y] = EL_TIME_ORB_EMPTY;
14757
14758       if (level.time > 0 || level.use_time_orb_bug)
14759       {
14760         TimeLeft += level.time_orb_time;
14761
14762 #if 1
14763         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14764
14765         DisplayGameControlValues();
14766 #else
14767         DrawGameValue_Time(TimeLeft);
14768 #endif
14769       }
14770
14771       ResetGfxAnimation(x, y);
14772       TEST_DrawLevelField(x, y);
14773     }
14774     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14775              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14776     {
14777       int xx, yy;
14778
14779       game.ball_state = !game.ball_state;
14780
14781       SCAN_PLAYFIELD(xx, yy)
14782       {
14783         int e = Feld[xx][yy];
14784
14785         if (game.ball_state)
14786         {
14787           if (e == EL_EMC_MAGIC_BALL)
14788             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14789           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14790             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14791         }
14792         else
14793         {
14794           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14795             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14796           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14797             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14798         }
14799       }
14800     }
14801
14802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14803                                         player->index_bit, dig_side);
14804
14805     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14806                                         player->index_bit, dig_side);
14807
14808     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14809                                         player->index_bit, dig_side);
14810
14811     return MP_ACTION;
14812   }
14813   else
14814   {
14815     if (!PLAYER_SWITCHING(player, x, y))
14816     {
14817       player->is_switching = TRUE;
14818       player->switch_x = x;
14819       player->switch_y = y;
14820
14821       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14822                                  player->index_bit, dig_side);
14823       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14824                                           player->index_bit, dig_side);
14825
14826       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14827                                  player->index_bit, dig_side);
14828       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14829                                           player->index_bit, dig_side);
14830     }
14831
14832     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14833                                player->index_bit, dig_side);
14834     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14835                                         player->index_bit, dig_side);
14836
14837     return MP_NO_ACTION;
14838   }
14839
14840   player->push_delay = -1;
14841
14842   if (is_player)                /* function can also be called by EL_PENGUIN */
14843   {
14844     if (Feld[x][y] != element)          /* really digged/collected something */
14845     {
14846       player->is_collecting = !player->is_digging;
14847       player->is_active = TRUE;
14848     }
14849   }
14850
14851   return MP_MOVING;
14852 }
14853
14854 static boolean DigFieldByCE(int x, int y, int digging_element)
14855 {
14856   int element = Feld[x][y];
14857
14858   if (!IS_FREE(x, y))
14859   {
14860     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14861                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14862                   ACTION_BREAKING);
14863
14864     /* no element can dig solid indestructible elements */
14865     if (IS_INDESTRUCTIBLE(element) &&
14866         !IS_DIGGABLE(element) &&
14867         !IS_COLLECTIBLE(element))
14868       return FALSE;
14869
14870     if (AmoebaNr[x][y] &&
14871         (element == EL_AMOEBA_FULL ||
14872          element == EL_BD_AMOEBA ||
14873          element == EL_AMOEBA_GROWING))
14874     {
14875       AmoebaCnt[AmoebaNr[x][y]]--;
14876       AmoebaCnt2[AmoebaNr[x][y]]--;
14877     }
14878
14879     if (IS_MOVING(x, y))
14880       RemoveMovingField(x, y);
14881     else
14882     {
14883       RemoveField(x, y);
14884       TEST_DrawLevelField(x, y);
14885     }
14886
14887     /* if digged element was about to explode, prevent the explosion */
14888     ExplodeField[x][y] = EX_TYPE_NONE;
14889
14890     PlayLevelSoundAction(x, y, action);
14891   }
14892
14893   Store[x][y] = EL_EMPTY;
14894
14895 #if 1
14896   /* this makes it possible to leave the removed element again */
14897   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14898     Store[x][y] = element;
14899 #else
14900   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14901   {
14902     int move_leave_element = element_info[digging_element].move_leave_element;
14903
14904     /* this makes it possible to leave the removed element again */
14905     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
14906                    element : move_leave_element);
14907   }
14908 #endif
14909
14910   return TRUE;
14911 }
14912
14913 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14914 {
14915   int jx = player->jx, jy = player->jy;
14916   int x = jx + dx, y = jy + dy;
14917   int snap_direction = (dx == -1 ? MV_LEFT  :
14918                         dx == +1 ? MV_RIGHT :
14919                         dy == -1 ? MV_UP    :
14920                         dy == +1 ? MV_DOWN  : MV_NONE);
14921   boolean can_continue_snapping = (level.continuous_snapping &&
14922                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14923
14924   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14925     return FALSE;
14926
14927   if (!player->active || !IN_LEV_FIELD(x, y))
14928     return FALSE;
14929
14930   if (dx && dy)
14931     return FALSE;
14932
14933   if (!dx && !dy)
14934   {
14935     if (player->MovPos == 0)
14936       player->is_pushing = FALSE;
14937
14938     player->is_snapping = FALSE;
14939
14940     if (player->MovPos == 0)
14941     {
14942       player->is_moving = FALSE;
14943       player->is_digging = FALSE;
14944       player->is_collecting = FALSE;
14945     }
14946
14947     return FALSE;
14948   }
14949
14950 #if USE_NEW_CONTINUOUS_SNAPPING
14951   /* prevent snapping with already pressed snap key when not allowed */
14952   if (player->is_snapping && !can_continue_snapping)
14953     return FALSE;
14954 #else
14955   if (player->is_snapping)
14956     return FALSE;
14957 #endif
14958
14959   player->MovDir = snap_direction;
14960
14961   if (player->MovPos == 0)
14962   {
14963     player->is_moving = FALSE;
14964     player->is_digging = FALSE;
14965     player->is_collecting = FALSE;
14966   }
14967
14968   player->is_dropping = FALSE;
14969   player->is_dropping_pressed = FALSE;
14970   player->drop_pressed_delay = 0;
14971
14972   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14973     return FALSE;
14974
14975   player->is_snapping = TRUE;
14976   player->is_active = TRUE;
14977
14978   if (player->MovPos == 0)
14979   {
14980     player->is_moving = FALSE;
14981     player->is_digging = FALSE;
14982     player->is_collecting = FALSE;
14983   }
14984
14985   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14986     TEST_DrawLevelField(player->last_jx, player->last_jy);
14987
14988   TEST_DrawLevelField(x, y);
14989
14990   return TRUE;
14991 }
14992
14993 static boolean DropElement(struct PlayerInfo *player)
14994 {
14995   int old_element, new_element;
14996   int dropx = player->jx, dropy = player->jy;
14997   int drop_direction = player->MovDir;
14998   int drop_side = drop_direction;
14999 #if 1
15000   int drop_element = get_next_dropped_element(player);
15001 #else
15002   int drop_element = (player->inventory_size > 0 ?
15003                       player->inventory_element[player->inventory_size - 1] :
15004                       player->inventory_infinite_element != EL_UNDEFINED ?
15005                       player->inventory_infinite_element :
15006                       player->dynabombs_left > 0 ?
15007                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15008                       EL_UNDEFINED);
15009 #endif
15010
15011   player->is_dropping_pressed = TRUE;
15012
15013   /* do not drop an element on top of another element; when holding drop key
15014      pressed without moving, dropped element must move away before the next
15015      element can be dropped (this is especially important if the next element
15016      is dynamite, which can be placed on background for historical reasons) */
15017   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15018     return MP_ACTION;
15019
15020   if (IS_THROWABLE(drop_element))
15021   {
15022     dropx += GET_DX_FROM_DIR(drop_direction);
15023     dropy += GET_DY_FROM_DIR(drop_direction);
15024
15025     if (!IN_LEV_FIELD(dropx, dropy))
15026       return FALSE;
15027   }
15028
15029   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15030   new_element = drop_element;           /* default: no change when dropping */
15031
15032   /* check if player is active, not moving and ready to drop */
15033   if (!player->active || player->MovPos || player->drop_delay > 0)
15034     return FALSE;
15035
15036   /* check if player has anything that can be dropped */
15037   if (new_element == EL_UNDEFINED)
15038     return FALSE;
15039
15040   /* check if drop key was pressed long enough for EM style dynamite */
15041   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15042     return FALSE;
15043
15044   /* check if anything can be dropped at the current position */
15045   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15046     return FALSE;
15047
15048   /* collected custom elements can only be dropped on empty fields */
15049   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15050     return FALSE;
15051
15052   if (old_element != EL_EMPTY)
15053     Back[dropx][dropy] = old_element;   /* store old element on this field */
15054
15055   ResetGfxAnimation(dropx, dropy);
15056   ResetRandomAnimationValue(dropx, dropy);
15057
15058   if (player->inventory_size > 0 ||
15059       player->inventory_infinite_element != EL_UNDEFINED)
15060   {
15061     if (player->inventory_size > 0)
15062     {
15063       player->inventory_size--;
15064
15065       DrawGameDoorValues();
15066
15067       if (new_element == EL_DYNAMITE)
15068         new_element = EL_DYNAMITE_ACTIVE;
15069       else if (new_element == EL_EM_DYNAMITE)
15070         new_element = EL_EM_DYNAMITE_ACTIVE;
15071       else if (new_element == EL_SP_DISK_RED)
15072         new_element = EL_SP_DISK_RED_ACTIVE;
15073     }
15074
15075     Feld[dropx][dropy] = new_element;
15076
15077     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15078       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15079                           el2img(Feld[dropx][dropy]), 0);
15080
15081     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15082
15083     /* needed if previous element just changed to "empty" in the last frame */
15084     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15085
15086     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15087                                player->index_bit, drop_side);
15088     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15089                                         CE_PLAYER_DROPS_X,
15090                                         player->index_bit, drop_side);
15091
15092     TestIfElementTouchesCustomElement(dropx, dropy);
15093   }
15094   else          /* player is dropping a dyna bomb */
15095   {
15096     player->dynabombs_left--;
15097
15098     Feld[dropx][dropy] = new_element;
15099
15100     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15101       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15102                           el2img(Feld[dropx][dropy]), 0);
15103
15104     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15105   }
15106
15107   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15108     InitField_WithBug1(dropx, dropy, FALSE);
15109
15110   new_element = Feld[dropx][dropy];     /* element might have changed */
15111
15112   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15113       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15114   {
15115     int move_direction, nextx, nexty;
15116
15117     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15118       MovDir[dropx][dropy] = drop_direction;
15119
15120     move_direction = MovDir[dropx][dropy];
15121     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15122     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15123
15124     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15125
15126 #if USE_FIX_IMPACT_COLLISION
15127     /* do not cause impact style collision by dropping elements that can fall */
15128     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15129 #else
15130     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15131 #endif
15132   }
15133
15134   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15135   player->is_dropping = TRUE;
15136
15137   player->drop_pressed_delay = 0;
15138   player->is_dropping_pressed = FALSE;
15139
15140   player->drop_x = dropx;
15141   player->drop_y = dropy;
15142
15143   return TRUE;
15144 }
15145
15146 /* ------------------------------------------------------------------------- */
15147 /* game sound playing functions                                              */
15148 /* ------------------------------------------------------------------------- */
15149
15150 static int *loop_sound_frame = NULL;
15151 static int *loop_sound_volume = NULL;
15152
15153 void InitPlayLevelSound()
15154 {
15155   int num_sounds = getSoundListSize();
15156
15157   checked_free(loop_sound_frame);
15158   checked_free(loop_sound_volume);
15159
15160   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15161   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15162 }
15163
15164 static void PlayLevelSound(int x, int y, int nr)
15165 {
15166   int sx = SCREENX(x), sy = SCREENY(y);
15167   int volume, stereo_position;
15168   int max_distance = 8;
15169   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15170
15171   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15172       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15173     return;
15174
15175   if (!IN_LEV_FIELD(x, y) ||
15176       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15177       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15178     return;
15179
15180   volume = SOUND_MAX_VOLUME;
15181
15182   if (!IN_SCR_FIELD(sx, sy))
15183   {
15184     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15185     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15186
15187     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15188   }
15189
15190   stereo_position = (SOUND_MAX_LEFT +
15191                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15192                      (SCR_FIELDX + 2 * max_distance));
15193
15194   if (IS_LOOP_SOUND(nr))
15195   {
15196     /* This assures that quieter loop sounds do not overwrite louder ones,
15197        while restarting sound volume comparison with each new game frame. */
15198
15199     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15200       return;
15201
15202     loop_sound_volume[nr] = volume;
15203     loop_sound_frame[nr] = FrameCounter;
15204   }
15205
15206   PlaySoundExt(nr, volume, stereo_position, type);
15207 }
15208
15209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15210 {
15211   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15212                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15213                  y < LEVELY(BY1) ? LEVELY(BY1) :
15214                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15215                  sound_action);
15216 }
15217
15218 static void PlayLevelSoundAction(int x, int y, int action)
15219 {
15220   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15221 }
15222
15223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15224 {
15225   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15226
15227   if (sound_effect != SND_UNDEFINED)
15228     PlayLevelSound(x, y, sound_effect);
15229 }
15230
15231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15232                                               int action)
15233 {
15234   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15235
15236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15237     PlayLevelSound(x, y, sound_effect);
15238 }
15239
15240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15241 {
15242   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15243
15244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15245     PlayLevelSound(x, y, sound_effect);
15246 }
15247
15248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15249 {
15250   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15251
15252   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15253     StopSound(sound_effect);
15254 }
15255
15256 static void PlayLevelMusic()
15257 {
15258   if (levelset.music[level_nr] != MUS_UNDEFINED)
15259     PlayMusic(levelset.music[level_nr]);        /* from config file */
15260   else
15261     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15262 }
15263
15264 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15265 {
15266   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15267   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15268   int x = xx - 1 - offset;
15269   int y = yy - 1 - offset;
15270
15271   switch (sample)
15272   {
15273     case SAMPLE_blank:
15274       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15275       break;
15276
15277     case SAMPLE_roll:
15278       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15279       break;
15280
15281     case SAMPLE_stone:
15282       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15283       break;
15284
15285     case SAMPLE_nut:
15286       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15287       break;
15288
15289     case SAMPLE_crack:
15290       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15291       break;
15292
15293     case SAMPLE_bug:
15294       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15295       break;
15296
15297     case SAMPLE_tank:
15298       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15299       break;
15300
15301     case SAMPLE_android_clone:
15302       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15303       break;
15304
15305     case SAMPLE_android_move:
15306       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15307       break;
15308
15309     case SAMPLE_spring:
15310       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15311       break;
15312
15313     case SAMPLE_slurp:
15314       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15315       break;
15316
15317     case SAMPLE_eater:
15318       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15319       break;
15320
15321     case SAMPLE_eater_eat:
15322       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15323       break;
15324
15325     case SAMPLE_alien:
15326       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15327       break;
15328
15329     case SAMPLE_collect:
15330       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15331       break;
15332
15333     case SAMPLE_diamond:
15334       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15335       break;
15336
15337     case SAMPLE_squash:
15338       /* !!! CHECK THIS !!! */
15339 #if 1
15340       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15341 #else
15342       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15343 #endif
15344       break;
15345
15346     case SAMPLE_wonderfall:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15348       break;
15349
15350     case SAMPLE_drip:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15352       break;
15353
15354     case SAMPLE_push:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15356       break;
15357
15358     case SAMPLE_dirt:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15360       break;
15361
15362     case SAMPLE_acid:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15364       break;
15365
15366     case SAMPLE_ball:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15368       break;
15369
15370     case SAMPLE_grow:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15372       break;
15373
15374     case SAMPLE_wonder:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15376       break;
15377
15378     case SAMPLE_door:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15380       break;
15381
15382     case SAMPLE_exit_open:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15384       break;
15385
15386     case SAMPLE_exit_leave:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15388       break;
15389
15390     case SAMPLE_dynamite:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15392       break;
15393
15394     case SAMPLE_tick:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15396       break;
15397
15398     case SAMPLE_press:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15400       break;
15401
15402     case SAMPLE_wheel:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15404       break;
15405
15406     case SAMPLE_boom:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15408       break;
15409
15410     case SAMPLE_die:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15412       break;
15413
15414     case SAMPLE_time:
15415       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15416       break;
15417
15418     default:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15420       break;
15421   }
15422 }
15423
15424 #if 0
15425 void ChangeTime(int value)
15426 {
15427   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15428
15429   *time += value;
15430
15431   /* EMC game engine uses value from time counter of RND game engine */
15432   level.native_em_level->lev->time = *time;
15433
15434   DrawGameValue_Time(*time);
15435 }
15436
15437 void RaiseScore(int value)
15438 {
15439   /* EMC game engine and RND game engine have separate score counters */
15440   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15441                 &level.native_em_level->lev->score : &local_player->score);
15442
15443   *score += value;
15444
15445   DrawGameValue_Score(*score);
15446 }
15447 #endif
15448
15449 void RaiseScore(int value)
15450 {
15451   local_player->score += value;
15452
15453 #if 1
15454   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15455
15456   DisplayGameControlValues();
15457 #else
15458   DrawGameValue_Score(local_player->score);
15459 #endif
15460 }
15461
15462 void RaiseScoreElement(int element)
15463 {
15464   switch (element)
15465   {
15466     case EL_EMERALD:
15467     case EL_BD_DIAMOND:
15468     case EL_EMERALD_YELLOW:
15469     case EL_EMERALD_RED:
15470     case EL_EMERALD_PURPLE:
15471     case EL_SP_INFOTRON:
15472       RaiseScore(level.score[SC_EMERALD]);
15473       break;
15474     case EL_DIAMOND:
15475       RaiseScore(level.score[SC_DIAMOND]);
15476       break;
15477     case EL_CRYSTAL:
15478       RaiseScore(level.score[SC_CRYSTAL]);
15479       break;
15480     case EL_PEARL:
15481       RaiseScore(level.score[SC_PEARL]);
15482       break;
15483     case EL_BUG:
15484     case EL_BD_BUTTERFLY:
15485     case EL_SP_ELECTRON:
15486       RaiseScore(level.score[SC_BUG]);
15487       break;
15488     case EL_SPACESHIP:
15489     case EL_BD_FIREFLY:
15490     case EL_SP_SNIKSNAK:
15491       RaiseScore(level.score[SC_SPACESHIP]);
15492       break;
15493     case EL_YAMYAM:
15494     case EL_DARK_YAMYAM:
15495       RaiseScore(level.score[SC_YAMYAM]);
15496       break;
15497     case EL_ROBOT:
15498       RaiseScore(level.score[SC_ROBOT]);
15499       break;
15500     case EL_PACMAN:
15501       RaiseScore(level.score[SC_PACMAN]);
15502       break;
15503     case EL_NUT:
15504       RaiseScore(level.score[SC_NUT]);
15505       break;
15506     case EL_DYNAMITE:
15507     case EL_EM_DYNAMITE:
15508     case EL_SP_DISK_RED:
15509     case EL_DYNABOMB_INCREASE_NUMBER:
15510     case EL_DYNABOMB_INCREASE_SIZE:
15511     case EL_DYNABOMB_INCREASE_POWER:
15512       RaiseScore(level.score[SC_DYNAMITE]);
15513       break;
15514     case EL_SHIELD_NORMAL:
15515     case EL_SHIELD_DEADLY:
15516       RaiseScore(level.score[SC_SHIELD]);
15517       break;
15518     case EL_EXTRA_TIME:
15519       RaiseScore(level.extra_time_score);
15520       break;
15521     case EL_KEY_1:
15522     case EL_KEY_2:
15523     case EL_KEY_3:
15524     case EL_KEY_4:
15525     case EL_EM_KEY_1:
15526     case EL_EM_KEY_2:
15527     case EL_EM_KEY_3:
15528     case EL_EM_KEY_4:
15529     case EL_EMC_KEY_5:
15530     case EL_EMC_KEY_6:
15531     case EL_EMC_KEY_7:
15532     case EL_EMC_KEY_8:
15533     case EL_DC_KEY_WHITE:
15534       RaiseScore(level.score[SC_KEY]);
15535       break;
15536     default:
15537       RaiseScore(element_info[element].collect_score);
15538       break;
15539   }
15540 }
15541
15542 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15543 {
15544   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15545   {
15546 #if defined(NETWORK_AVALIABLE)
15547     if (options.network)
15548       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15549     else
15550 #endif
15551     {
15552       if (quick_quit)
15553       {
15554 #if 1
15555
15556 #if 1
15557         FadeSkipNextFadeIn();
15558 #else
15559         fading = fading_none;
15560 #endif
15561
15562 #else
15563         OpenDoor(DOOR_CLOSE_1);
15564 #endif
15565
15566         game_status = GAME_MODE_MAIN;
15567
15568 #if 1
15569         DrawAndFadeInMainMenu(REDRAW_FIELD);
15570 #else
15571         DrawMainMenu();
15572 #endif
15573       }
15574       else
15575       {
15576 #if 0
15577         FadeOut(REDRAW_FIELD);
15578 #endif
15579
15580         game_status = GAME_MODE_MAIN;
15581
15582         DrawAndFadeInMainMenu(REDRAW_FIELD);
15583       }
15584     }
15585   }
15586   else          /* continue playing the game */
15587   {
15588     if (tape.playing && tape.deactivate_display)
15589       TapeDeactivateDisplayOff(TRUE);
15590
15591     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15592
15593     if (tape.playing && tape.deactivate_display)
15594       TapeDeactivateDisplayOn();
15595   }
15596 }
15597
15598 void RequestQuitGame(boolean ask_if_really_quit)
15599 {
15600   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15601   boolean skip_request = AllPlayersGone || quick_quit;
15602
15603   RequestQuitGameExt(skip_request, quick_quit,
15604                      "Do you really want to quit the game ?");
15605 }
15606
15607
15608 /* ------------------------------------------------------------------------- */
15609 /* random generator functions                                                */
15610 /* ------------------------------------------------------------------------- */
15611
15612 unsigned int InitEngineRandom_RND(long seed)
15613 {
15614   game.num_random_calls = 0;
15615
15616 #if 0
15617   unsigned int rnd_seed = InitEngineRandom(seed);
15618
15619   printf("::: START RND: %d\n", rnd_seed);
15620
15621   return rnd_seed;
15622 #else
15623
15624   return InitEngineRandom(seed);
15625
15626 #endif
15627
15628 }
15629
15630 unsigned int RND(int max)
15631 {
15632   if (max > 0)
15633   {
15634     game.num_random_calls++;
15635
15636     return GetEngineRandom(max);
15637   }
15638
15639   return 0;
15640 }
15641
15642
15643 /* ------------------------------------------------------------------------- */
15644 /* game engine snapshot handling functions                                   */
15645 /* ------------------------------------------------------------------------- */
15646
15647 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15648
15649 struct EngineSnapshotInfo
15650 {
15651   /* runtime values for custom element collect score */
15652   int collect_score[NUM_CUSTOM_ELEMENTS];
15653
15654   /* runtime values for group element choice position */
15655   int choice_pos[NUM_GROUP_ELEMENTS];
15656
15657   /* runtime values for belt position animations */
15658   int belt_graphic[4 * NUM_BELT_PARTS];
15659   int belt_anim_mode[4 * NUM_BELT_PARTS];
15660 };
15661
15662 struct EngineSnapshotNodeInfo
15663 {
15664   void *buffer_orig;
15665   void *buffer_copy;
15666   int size;
15667 };
15668
15669 static struct EngineSnapshotInfo engine_snapshot_rnd;
15670 static ListNode *engine_snapshot_list = NULL;
15671 static char *snapshot_level_identifier = NULL;
15672 static int snapshot_level_nr = -1;
15673
15674 void FreeEngineSnapshot()
15675 {
15676   while (engine_snapshot_list != NULL)
15677     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15678                        checked_free);
15679
15680   setString(&snapshot_level_identifier, NULL);
15681   snapshot_level_nr = -1;
15682 }
15683
15684 static void SaveEngineSnapshotValues_RND()
15685 {
15686   static int belt_base_active_element[4] =
15687   {
15688     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15689     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15690     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15691     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15692   };
15693   int i, j;
15694
15695   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15696   {
15697     int element = EL_CUSTOM_START + i;
15698
15699     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15700   }
15701
15702   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15703   {
15704     int element = EL_GROUP_START + i;
15705
15706     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15707   }
15708
15709   for (i = 0; i < 4; i++)
15710   {
15711     for (j = 0; j < NUM_BELT_PARTS; j++)
15712     {
15713       int element = belt_base_active_element[i] + j;
15714       int graphic = el2img(element);
15715       int anim_mode = graphic_info[graphic].anim_mode;
15716
15717       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15718       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15719     }
15720   }
15721 }
15722
15723 static void LoadEngineSnapshotValues_RND()
15724 {
15725   unsigned long num_random_calls = game.num_random_calls;
15726   int i, j;
15727
15728   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15729   {
15730     int element = EL_CUSTOM_START + i;
15731
15732     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15733   }
15734
15735   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15736   {
15737     int element = EL_GROUP_START + i;
15738
15739     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15740   }
15741
15742   for (i = 0; i < 4; i++)
15743   {
15744     for (j = 0; j < NUM_BELT_PARTS; j++)
15745     {
15746       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15747       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15748
15749       graphic_info[graphic].anim_mode = anim_mode;
15750     }
15751   }
15752
15753   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15754   {
15755     InitRND(tape.random_seed);
15756     for (i = 0; i < num_random_calls; i++)
15757       RND(1);
15758   }
15759
15760   if (game.num_random_calls != num_random_calls)
15761   {
15762     Error(ERR_INFO, "number of random calls out of sync");
15763     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15764     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15765     Error(ERR_EXIT, "this should not happen -- please debug");
15766   }
15767 }
15768
15769 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15770 {
15771   struct EngineSnapshotNodeInfo *bi =
15772     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15773
15774   bi->buffer_orig = buffer;
15775   bi->buffer_copy = checked_malloc(size);
15776   bi->size = size;
15777
15778   memcpy(bi->buffer_copy, buffer, size);
15779
15780   addNodeToList(&engine_snapshot_list, NULL, bi);
15781 }
15782
15783 void SaveEngineSnapshot()
15784 {
15785   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15786
15787   if (level_editor_test_game)   /* do not save snapshots from editor */
15788     return;
15789
15790   /* copy some special values to a structure better suited for the snapshot */
15791
15792   SaveEngineSnapshotValues_RND();
15793   SaveEngineSnapshotValues_EM();
15794
15795   /* save values stored in special snapshot structure */
15796
15797   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15799
15800   /* save further RND engine values */
15801
15802   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15804   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15805
15806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15807   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15808   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15810
15811   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15813   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15814   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15815   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15816
15817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15818   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15819   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15820
15821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15822
15823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15824
15825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15827
15828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15837   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15840   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15844   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15846
15847   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15849
15850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15853
15854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15856
15857   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15858   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15859   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15860   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15861   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15862
15863   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15864   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15865
15866   /* save level identification information */
15867
15868   setString(&snapshot_level_identifier, leveldir_current->identifier);
15869   snapshot_level_nr = level_nr;
15870
15871 #if 0
15872   ListNode *node = engine_snapshot_list;
15873   int num_bytes = 0;
15874
15875   while (node != NULL)
15876   {
15877     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15878
15879     node = node->next;
15880   }
15881
15882   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15883 #endif
15884 }
15885
15886 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15887 {
15888   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15889 }
15890
15891 void LoadEngineSnapshot()
15892 {
15893   ListNode *node = engine_snapshot_list;
15894
15895   if (engine_snapshot_list == NULL)
15896     return;
15897
15898   while (node != NULL)
15899   {
15900     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15901
15902     node = node->next;
15903   }
15904
15905   /* restore special values from snapshot structure */
15906
15907   LoadEngineSnapshotValues_RND();
15908   LoadEngineSnapshotValues_EM();
15909 }
15910
15911 boolean CheckEngineSnapshot()
15912 {
15913   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15914           snapshot_level_nr == level_nr);
15915 }
15916
15917
15918 /* ---------- new game button stuff ---------------------------------------- */
15919
15920 /* graphic position values for game buttons */
15921 #define GAME_BUTTON_XSIZE       30
15922 #define GAME_BUTTON_YSIZE       30
15923 #define GAME_BUTTON_XPOS        5
15924 #define GAME_BUTTON_YPOS        215
15925 #define SOUND_BUTTON_XPOS       5
15926 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15927
15928 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15929 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15930 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15931 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15932 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15933 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15934
15935 static struct
15936 {
15937   int *x, *y;
15938   int gd_x, gd_y;
15939   int gadget_id;
15940   char *infotext;
15941 } gamebutton_info[NUM_GAME_BUTTONS] =
15942 {
15943 #if 1
15944   {
15945     &game.button.stop.x,        &game.button.stop.y,
15946     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15947     GAME_CTRL_ID_STOP,
15948     "stop game"
15949   },
15950   {
15951     &game.button.pause.x,       &game.button.pause.y,
15952     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15953     GAME_CTRL_ID_PAUSE,
15954     "pause game"
15955   },
15956   {
15957     &game.button.play.x,        &game.button.play.y,
15958     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15959     GAME_CTRL_ID_PLAY,
15960     "play game"
15961   },
15962   {
15963     &game.button.sound_music.x, &game.button.sound_music.y,
15964     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15965     SOUND_CTRL_ID_MUSIC,
15966     "background music on/off"
15967   },
15968   {
15969     &game.button.sound_loops.x, &game.button.sound_loops.y,
15970     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15971     SOUND_CTRL_ID_LOOPS,
15972     "sound loops on/off"
15973   },
15974   {
15975     &game.button.sound_simple.x,&game.button.sound_simple.y,
15976     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15977     SOUND_CTRL_ID_SIMPLE,
15978     "normal sounds on/off"
15979   }
15980 #else
15981   {
15982     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15983     GAME_CTRL_ID_STOP,
15984     "stop game"
15985   },
15986   {
15987     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15988     GAME_CTRL_ID_PAUSE,
15989     "pause game"
15990   },
15991   {
15992     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15993     GAME_CTRL_ID_PLAY,
15994     "play game"
15995   },
15996   {
15997     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15998     SOUND_CTRL_ID_MUSIC,
15999     "background music on/off"
16000   },
16001   {
16002     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16003     SOUND_CTRL_ID_LOOPS,
16004     "sound loops on/off"
16005   },
16006   {
16007     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16008     SOUND_CTRL_ID_SIMPLE,
16009     "normal sounds on/off"
16010   }
16011 #endif
16012 };
16013
16014 void CreateGameButtons()
16015 {
16016   int i;
16017
16018   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16019   {
16020     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16021     struct GadgetInfo *gi;
16022     int button_type;
16023     boolean checked;
16024     unsigned long event_mask;
16025     int x, y;
16026     int gd_xoffset, gd_yoffset;
16027     int gd_x1, gd_x2, gd_y1, gd_y2;
16028     int id = i;
16029
16030     x = DX + *gamebutton_info[i].x;
16031     y = DY + *gamebutton_info[i].y;
16032     gd_xoffset = gamebutton_info[i].gd_x;
16033     gd_yoffset = gamebutton_info[i].gd_y;
16034     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16035     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16036
16037     if (id == GAME_CTRL_ID_STOP ||
16038         id == GAME_CTRL_ID_PAUSE ||
16039         id == GAME_CTRL_ID_PLAY)
16040     {
16041       button_type = GD_TYPE_NORMAL_BUTTON;
16042       checked = FALSE;
16043       event_mask = GD_EVENT_RELEASED;
16044       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16045       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16046     }
16047     else
16048     {
16049       button_type = GD_TYPE_CHECK_BUTTON;
16050       checked =
16051         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16052          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16053          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16054       event_mask = GD_EVENT_PRESSED;
16055       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16056       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16057     }
16058
16059     gi = CreateGadget(GDI_CUSTOM_ID, id,
16060                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16061 #if 1
16062                       GDI_X, x,
16063                       GDI_Y, y,
16064 #else
16065                       GDI_X, DX + gd_xoffset,
16066                       GDI_Y, DY + gd_yoffset,
16067 #endif
16068                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16069                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16070                       GDI_TYPE, button_type,
16071                       GDI_STATE, GD_BUTTON_UNPRESSED,
16072                       GDI_CHECKED, checked,
16073                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16074                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16075                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16076                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16077                       GDI_DIRECT_DRAW, FALSE,
16078                       GDI_EVENT_MASK, event_mask,
16079                       GDI_CALLBACK_ACTION, HandleGameButtons,
16080                       GDI_END);
16081
16082     if (gi == NULL)
16083       Error(ERR_EXIT, "cannot create gadget");
16084
16085     game_gadget[id] = gi;
16086   }
16087 }
16088
16089 void FreeGameButtons()
16090 {
16091   int i;
16092
16093   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16094     FreeGadget(game_gadget[i]);
16095 }
16096
16097 static void MapGameButtons()
16098 {
16099   int i;
16100
16101   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16102     MapGadget(game_gadget[i]);
16103 }
16104
16105 void UnmapGameButtons()
16106 {
16107   int i;
16108
16109   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16110     UnmapGadget(game_gadget[i]);
16111 }
16112
16113 void RedrawGameButtons()
16114 {
16115   int i;
16116
16117   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16118     RedrawGadget(game_gadget[i]);
16119 }
16120
16121 static void HandleGameButtons(struct GadgetInfo *gi)
16122 {
16123   int id = gi->custom_id;
16124
16125   if (game_status != GAME_MODE_PLAYING)
16126     return;
16127
16128   switch (id)
16129   {
16130     case GAME_CTRL_ID_STOP:
16131       if (tape.playing)
16132         TapeStop();
16133       else
16134         RequestQuitGame(TRUE);
16135       break;
16136
16137     case GAME_CTRL_ID_PAUSE:
16138       if (options.network)
16139       {
16140 #if defined(NETWORK_AVALIABLE)
16141         if (tape.pausing)
16142           SendToServer_ContinuePlaying();
16143         else
16144           SendToServer_PausePlaying();
16145 #endif
16146       }
16147       else
16148         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16149       break;
16150
16151     case GAME_CTRL_ID_PLAY:
16152       if (tape.pausing)
16153       {
16154 #if defined(NETWORK_AVALIABLE)
16155         if (options.network)
16156           SendToServer_ContinuePlaying();
16157         else
16158 #endif
16159         {
16160           tape.pausing = FALSE;
16161           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16162         }
16163       }
16164       break;
16165
16166     case SOUND_CTRL_ID_MUSIC:
16167       if (setup.sound_music)
16168       { 
16169         setup.sound_music = FALSE;
16170         FadeMusic();
16171       }
16172       else if (audio.music_available)
16173       { 
16174         setup.sound = setup.sound_music = TRUE;
16175
16176         SetAudioMode(setup.sound);
16177
16178         PlayLevelMusic();
16179       }
16180       break;
16181
16182     case SOUND_CTRL_ID_LOOPS:
16183       if (setup.sound_loops)
16184         setup.sound_loops = FALSE;
16185       else if (audio.loops_available)
16186       {
16187         setup.sound = setup.sound_loops = TRUE;
16188         SetAudioMode(setup.sound);
16189       }
16190       break;
16191
16192     case SOUND_CTRL_ID_SIMPLE:
16193       if (setup.sound_simple)
16194         setup.sound_simple = FALSE;
16195       else if (audio.sound_available)
16196       {
16197         setup.sound = setup.sound_simple = TRUE;
16198         SetAudioMode(setup.sound);
16199       }
16200       break;
16201
16202     default:
16203       break;
16204   }
16205 }