rnd-20100220-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active &&
4232           !tape.player_participates[i])
4233       {
4234         struct PlayerInfo *player = &stored_player[i];
4235         int jx = player->jx, jy = player->jy;
4236
4237         player->active = FALSE;
4238         StorePlayer[jx][jy] = 0;
4239         Feld[jx][jy] = EL_EMPTY;
4240       }
4241     }
4242 #endif
4243   }
4244   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4245   {
4246     /* when in single player mode, eliminate all but the first active player */
4247
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249     {
4250       if (stored_player[i].active)
4251       {
4252         for (j = i + 1; j < MAX_PLAYERS; j++)
4253         {
4254           if (stored_player[j].active)
4255           {
4256             struct PlayerInfo *player = &stored_player[j];
4257             int jx = player->jx, jy = player->jy;
4258
4259             player->active = FALSE;
4260             player->present = FALSE;
4261
4262             StorePlayer[jx][jy] = 0;
4263             Feld[jx][jy] = EL_EMPTY;
4264           }
4265         }
4266       }
4267     }
4268   }
4269
4270   /* when recording the game, store which players take part in the game */
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284   if (options.debug)
4285   {
4286     for (i = 0; i < MAX_PLAYERS; i++)
4287     {
4288       struct PlayerInfo *player = &stored_player[i];
4289
4290       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4291              i+1,
4292              player->present,
4293              player->connected,
4294              player->active);
4295       if (local_player == player)
4296         printf("Player  %d is local player.\n", i+1);
4297     }
4298   }
4299
4300   if (BorderElement == EL_EMPTY)
4301   {
4302     SBX_Left = 0;
4303     SBX_Right = lev_fieldx - SCR_FIELDX;
4304     SBY_Upper = 0;
4305     SBY_Lower = lev_fieldy - SCR_FIELDY;
4306   }
4307   else
4308   {
4309     SBX_Left = -1;
4310     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311     SBY_Upper = -1;
4312     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4313   }
4314
4315   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320
4321   /* if local player not found, look for custom element that might create
4322      the player (make some assumptions about the right custom element) */
4323   if (!local_player->present)
4324   {
4325     int start_x = 0, start_y = 0;
4326     int found_rating = 0;
4327     int found_element = EL_UNDEFINED;
4328     int player_nr = local_player->index_nr;
4329
4330     SCAN_PLAYFIELD(x, y)
4331     {
4332       int element = Feld[x][y];
4333       int content;
4334       int xx, yy;
4335       boolean is_player;
4336
4337       if (level.use_start_element[player_nr] &&
4338           level.start_element[player_nr] == element &&
4339           found_rating < 4)
4340       {
4341         start_x = x;
4342         start_y = y;
4343
4344         found_rating = 4;
4345         found_element = element;
4346       }
4347
4348       if (!IS_CUSTOM_ELEMENT(element))
4349         continue;
4350
4351       if (CAN_CHANGE(element))
4352       {
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as single target */
4356           content = element_info[element].change_page[i].target_element;
4357           is_player = ELEM_IS_PLAYER(content);
4358
4359           if (is_player && (found_rating < 3 ||
4360                             (found_rating == 3 && element < found_element)))
4361           {
4362             start_x = x;
4363             start_y = y;
4364
4365             found_rating = 3;
4366             found_element = element;
4367           }
4368         }
4369       }
4370
4371       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372       {
4373         /* check for player created from custom element as explosion content */
4374         content = element_info[element].content.e[xx][yy];
4375         is_player = ELEM_IS_PLAYER(content);
4376
4377         if (is_player && (found_rating < 2 ||
4378                           (found_rating == 2 && element < found_element)))
4379         {
4380           start_x = x + xx - 1;
4381           start_y = y + yy - 1;
4382
4383           found_rating = 2;
4384           found_element = element;
4385         }
4386
4387         if (!CAN_CHANGE(element))
4388           continue;
4389
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           /* check for player created from custom element as extended target */
4393           content =
4394             element_info[element].change_page[i].target_content.e[xx][yy];
4395
4396           is_player = ELEM_IS_PLAYER(content);
4397
4398           if (is_player && (found_rating < 1 ||
4399                             (found_rating == 1 && element < found_element)))
4400           {
4401             start_x = x + xx - 1;
4402             start_y = y + yy - 1;
4403
4404             found_rating = 1;
4405             found_element = element;
4406           }
4407         }
4408       }
4409     }
4410
4411     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4412                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4413                 start_x - MIDPOSX);
4414
4415     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4417                 start_y - MIDPOSY);
4418   }
4419   else
4420   {
4421     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4422                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423                 local_player->jx - MIDPOSX);
4424
4425     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427                 local_player->jy - MIDPOSY);
4428   }
4429
4430 #if 0
4431   /* do not use PLAYING mask for fading out from main screen */
4432   game_status = GAME_MODE_MAIN;
4433 #endif
4434
4435   StopAnimation();
4436
4437   if (!game.restart_level)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440 #if 1
4441   if (level_editor_test_game)
4442     FadeSkipNextFadeIn();
4443   else
4444     FadeSetEnterScreen();
4445 #else
4446   if (level_editor_test_game)
4447     fading = fading_none;
4448   else
4449     fading = menu.destination;
4450 #endif
4451
4452 #if 1
4453   FadeOut(REDRAW_FIELD);
4454 #else
4455   if (do_fading)
4456     FadeOut(REDRAW_FIELD);
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4472   {
4473     InitGameEngine_SP();
4474
4475     /* blit playfield from scroll buffer to normal back buffer for fading in */
4476     BlitScreenToBitmap_SP(backbuffer);
4477   }
4478   else
4479   {
4480     DrawLevel();
4481     DrawAllPlayers();
4482
4483     /* after drawing the level, correct some elements */
4484     if (game.timegate_time_left == 0)
4485       CloseAllOpenTimegates();
4486
4487     /* blit playfield from scroll buffer to normal back buffer for fading in */
4488     if (setup.soft_scrolling)
4489       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4490
4491     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4492   }
4493   /* !!! FIX THIS (END) !!! */
4494
4495 #if 1
4496   FadeIn(REDRAW_FIELD);
4497 #else
4498   if (do_fading)
4499     FadeIn(REDRAW_FIELD);
4500
4501   BackToFront();
4502 #endif
4503
4504   if (!game.restart_level)
4505   {
4506     /* copy default game door content to main double buffer */
4507     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4509   }
4510
4511   SetPanelBackground();
4512   SetDrawBackgroundMask(REDRAW_DOOR_1);
4513
4514 #if 1
4515   UpdateAndDisplayGameControlValues();
4516 #else
4517   UpdateGameDoorValues();
4518   DrawGameDoorValues();
4519 #endif
4520
4521   if (!game.restart_level)
4522   {
4523     UnmapGameButtons();
4524     UnmapTapeButtons();
4525     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4528     MapGameButtons();
4529     MapTapeButtons();
4530
4531     /* copy actual game door content to door double buffer for OpenDoor() */
4532     BlitBitmap(drawto, bitmap_db_door,
4533                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4534
4535     OpenDoor(DOOR_OPEN_ALL);
4536
4537     PlaySound(SND_GAME_STARTING);
4538
4539     if (setup.sound_music)
4540       PlayLevelMusic();
4541
4542     KeyboardAutoRepeatOffUnlessAutoplay();
4543
4544     if (options.debug)
4545     {
4546       for (i = 0; i < MAX_PLAYERS; i++)
4547         printf("Player %d %sactive.\n",
4548                i + 1, (stored_player[i].active ? "" : "not "));
4549     }
4550   }
4551
4552 #if 1
4553   UnmapAllGadgets();
4554
4555   MapGameButtons();
4556   MapTapeButtons();
4557 #endif
4558
4559   game.restart_level = FALSE;
4560 }
4561
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4563 {
4564   /* this is used for non-R'n'D game engines to update certain engine values */
4565
4566   /* needed to determine if sounds are played within the visible screen area */
4567   scroll_x = actual_scroll_x;
4568   scroll_y = actual_scroll_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Feld[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Feld[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Feld[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Feld[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Feld[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Feld[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Feld[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Feld[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     default:
4655       if (IS_CUSTOM_ELEMENT(element))
4656       {
4657         struct ElementInfo *ei = &element_info[element];
4658         int move_direction_initial = ei->move_direction_initial;
4659         int move_pattern = ei->move_pattern;
4660
4661         if (move_direction_initial == MV_START_PREVIOUS)
4662         {
4663           if (MovDir[x][y] != MV_NONE)
4664             return;
4665
4666           move_direction_initial = MV_START_AUTOMATIC;
4667         }
4668
4669         if (move_direction_initial == MV_START_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_direction_initial & MV_ANY_DIRECTION)
4672           MovDir[x][y] = move_direction_initial;
4673         else if (move_pattern == MV_ALL_DIRECTIONS ||
4674                  move_pattern == MV_TURNING_LEFT ||
4675                  move_pattern == MV_TURNING_RIGHT ||
4676                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4677                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4678                  move_pattern == MV_TURNING_RANDOM)
4679           MovDir[x][y] = 1 << RND(4);
4680         else if (move_pattern == MV_HORIZONTAL)
4681           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682         else if (move_pattern == MV_VERTICAL)
4683           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684         else if (move_pattern & MV_ANY_DIRECTION)
4685           MovDir[x][y] = element_info[element].move_pattern;
4686         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687                  move_pattern == MV_ALONG_RIGHT_SIDE)
4688         {
4689           /* use random direction as default start direction */
4690           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691             MovDir[x][y] = 1 << RND(4);
4692
4693           for (i = 0; i < NUM_DIRECTIONS; i++)
4694           {
4695             int x1 = x + xy[i][0];
4696             int y1 = y + xy[i][1];
4697
4698             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4699             {
4700               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701                 MovDir[x][y] = direction[0][i];
4702               else
4703                 MovDir[x][y] = direction[1][i];
4704
4705               break;
4706             }
4707           }
4708         }                
4709       }
4710       else
4711       {
4712         MovDir[x][y] = 1 << RND(4);
4713
4714         if (element != EL_BUG &&
4715             element != EL_SPACESHIP &&
4716             element != EL_BD_BUTTERFLY &&
4717             element != EL_BD_FIREFLY)
4718           break;
4719
4720         for (i = 0; i < NUM_DIRECTIONS; i++)
4721         {
4722           int x1 = x + xy[i][0];
4723           int y1 = y + xy[i][1];
4724
4725           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4726           {
4727             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4728             {
4729               MovDir[x][y] = direction[0][i];
4730               break;
4731             }
4732             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4734             {
4735               MovDir[x][y] = direction[1][i];
4736               break;
4737             }
4738           }
4739         }
4740       }
4741       break;
4742   }
4743
4744   GfxDir[x][y] = MovDir[x][y];
4745 }
4746
4747 void InitAmoebaNr(int x, int y)
4748 {
4749   int i;
4750   int group_nr = AmoebeNachbarNr(x, y);
4751
4752   if (group_nr == 0)
4753   {
4754     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4755     {
4756       if (AmoebaCnt[i] == 0)
4757       {
4758         group_nr = i;
4759         break;
4760       }
4761     }
4762   }
4763
4764   AmoebaNr[x][y] = group_nr;
4765   AmoebaCnt[group_nr]++;
4766   AmoebaCnt2[group_nr]++;
4767 }
4768
4769 static void PlayerWins(struct PlayerInfo *player)
4770 {
4771   player->LevelSolved = TRUE;
4772   player->GameOver = TRUE;
4773
4774   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775                          level.native_em_level->lev->score : player->score);
4776
4777   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778   player->LevelSolved_CountingScore = player->score_final;
4779 }
4780
4781 void GameWon()
4782 {
4783   static int time, time_final;
4784   static int score, score_final;
4785   static int game_over_delay_1 = 0;
4786   static int game_over_delay_2 = 0;
4787   int game_over_delay_value_1 = 50;
4788   int game_over_delay_value_2 = 50;
4789
4790   if (!local_player->LevelSolved_GameWon)
4791   {
4792     int i;
4793
4794     /* do not start end game actions before the player stops moving (to exit) */
4795     if (local_player->MovPos)
4796       return;
4797
4798     local_player->LevelSolved_GameWon = TRUE;
4799     local_player->LevelSolved_SaveTape = tape.recording;
4800     local_player->LevelSolved_SaveScore = !tape.playing;
4801
4802     if (tape.auto_play)         /* tape might already be stopped here */
4803       tape.auto_play_level_solved = TRUE;
4804
4805 #if 1
4806     TapeStop();
4807 #endif
4808
4809     game_over_delay_1 = game_over_delay_value_1;
4810     game_over_delay_2 = game_over_delay_value_2;
4811
4812     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813     score = score_final = local_player->score_final;
4814
4815     if (TimeLeft > 0)
4816     {
4817       time_final = 0;
4818       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4819     }
4820     else if (level.time == 0 && TimePlayed < 999)
4821     {
4822       time_final = 999;
4823       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4824     }
4825
4826     local_player->score_final = score_final;
4827
4828     if (level_editor_test_game)
4829     {
4830       time = time_final;
4831       score = score_final;
4832
4833 #if 1
4834       local_player->LevelSolved_CountingTime = time;
4835       local_player->LevelSolved_CountingScore = score;
4836
4837       game_panel_controls[GAME_PANEL_TIME].value = time;
4838       game_panel_controls[GAME_PANEL_SCORE].value = score;
4839
4840       DisplayGameControlValues();
4841 #else
4842       DrawGameValue_Time(time);
4843       DrawGameValue_Score(score);
4844 #endif
4845     }
4846
4847     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4848     {
4849       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4850       {
4851         /* close exit door after last player */
4852         if ((AllPlayersGone &&
4853              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4858         {
4859           int element = Feld[ExitX][ExitY];
4860
4861 #if 0
4862           if (element == EL_EM_EXIT_OPEN ||
4863               element == EL_EM_STEEL_EXIT_OPEN)
4864           {
4865             Bang(ExitX, ExitY);
4866           }
4867           else
4868 #endif
4869           {
4870             Feld[ExitX][ExitY] =
4871               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4872                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4873                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4874                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4875                EL_EM_STEEL_EXIT_CLOSING);
4876
4877             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4878           }
4879         }
4880
4881         /* player disappears */
4882         DrawLevelField(ExitX, ExitY);
4883       }
4884
4885       for (i = 0; i < MAX_PLAYERS; i++)
4886       {
4887         struct PlayerInfo *player = &stored_player[i];
4888
4889         if (player->present)
4890         {
4891           RemovePlayer(player);
4892
4893           /* player disappears */
4894           DrawLevelField(player->jx, player->jy);
4895         }
4896       }
4897     }
4898
4899     PlaySound(SND_GAME_WINNING);
4900   }
4901
4902   if (game_over_delay_1 > 0)
4903   {
4904     game_over_delay_1--;
4905
4906     return;
4907   }
4908
4909   if (time != time_final)
4910   {
4911     int time_to_go = ABS(time_final - time);
4912     int time_count_dir = (time < time_final ? +1 : -1);
4913     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4914
4915     time  += time_count_steps * time_count_dir;
4916     score += time_count_steps * level.score[SC_TIME_BONUS];
4917
4918 #if 1
4919     local_player->LevelSolved_CountingTime = time;
4920     local_player->LevelSolved_CountingScore = score;
4921
4922     game_panel_controls[GAME_PANEL_TIME].value = time;
4923     game_panel_controls[GAME_PANEL_SCORE].value = score;
4924
4925     DisplayGameControlValues();
4926 #else
4927     DrawGameValue_Time(time);
4928     DrawGameValue_Score(score);
4929 #endif
4930
4931     if (time == time_final)
4932       StopSound(SND_GAME_LEVELTIME_BONUS);
4933     else if (setup.sound_loops)
4934       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4935     else
4936       PlaySound(SND_GAME_LEVELTIME_BONUS);
4937
4938     return;
4939   }
4940
4941   local_player->LevelSolved_PanelOff = TRUE;
4942
4943   if (game_over_delay_2 > 0)
4944   {
4945     game_over_delay_2--;
4946
4947     return;
4948   }
4949
4950 #if 1
4951   GameEnd();
4952 #endif
4953 }
4954
4955 void GameEnd()
4956 {
4957   int hi_pos;
4958   boolean raise_level = FALSE;
4959
4960   local_player->LevelSolved_GameEnd = TRUE;
4961
4962   CloseDoor(DOOR_CLOSE_1);
4963
4964   if (local_player->LevelSolved_SaveTape)
4965   {
4966 #if 0
4967     TapeStop();
4968 #endif
4969
4970 #if 1
4971     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4972 #else
4973     SaveTape(tape.level_nr);            /* ask to save tape */
4974 #endif
4975   }
4976
4977   if (level_editor_test_game)
4978   {
4979     game_status = GAME_MODE_MAIN;
4980
4981 #if 1
4982     DrawAndFadeInMainMenu(REDRAW_FIELD);
4983 #else
4984     DrawMainMenu();
4985 #endif
4986
4987     return;
4988   }
4989
4990   if (!local_player->LevelSolved_SaveScore)
4991   {
4992 #if 1
4993     FadeOut(REDRAW_FIELD);
4994 #endif
4995
4996     game_status = GAME_MODE_MAIN;
4997
4998     DrawAndFadeInMainMenu(REDRAW_FIELD);
4999
5000     return;
5001   }
5002
5003   if (level_nr == leveldir_current->handicap_level)
5004   {
5005     leveldir_current->handicap_level++;
5006     SaveLevelSetup_SeriesInfo();
5007   }
5008
5009   if (level_nr < leveldir_current->last_level)
5010     raise_level = TRUE;                 /* advance to next level */
5011
5012   if ((hi_pos = NewHiScore()) >= 0) 
5013   {
5014     game_status = GAME_MODE_SCORES;
5015
5016     DrawHallOfFame(hi_pos);
5017
5018     if (raise_level)
5019     {
5020       level_nr++;
5021       TapeErase();
5022     }
5023   }
5024   else
5025   {
5026 #if 1
5027     FadeOut(REDRAW_FIELD);
5028 #endif
5029
5030     game_status = GAME_MODE_MAIN;
5031
5032     if (raise_level)
5033     {
5034       level_nr++;
5035       TapeErase();
5036     }
5037
5038     DrawAndFadeInMainMenu(REDRAW_FIELD);
5039   }
5040 }
5041
5042 int NewHiScore()
5043 {
5044   int k, l;
5045   int position = -1;
5046
5047   LoadScore(level_nr);
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5051     return -1;
5052
5053   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5054   {
5055     if (local_player->score_final > highscore[k].Score)
5056     {
5057       /* player has made it to the hall of fame */
5058
5059       if (k < MAX_SCORE_ENTRIES - 1)
5060       {
5061         int m = MAX_SCORE_ENTRIES - 1;
5062
5063 #ifdef ONE_PER_NAME
5064         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065           if (strEqual(setup.player_name, highscore[l].Name))
5066             m = l;
5067         if (m == k)     /* player's new highscore overwrites his old one */
5068           goto put_into_list;
5069 #endif
5070
5071         for (l = m; l > k; l--)
5072         {
5073           strcpy(highscore[l].Name, highscore[l - 1].Name);
5074           highscore[l].Score = highscore[l - 1].Score;
5075         }
5076       }
5077
5078 #ifdef ONE_PER_NAME
5079       put_into_list:
5080 #endif
5081       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083       highscore[k].Score = local_player->score_final; 
5084       position = k;
5085       break;
5086     }
5087
5088 #ifdef ONE_PER_NAME
5089     else if (!strncmp(setup.player_name, highscore[k].Name,
5090                       MAX_PLAYER_NAME_LEN))
5091       break;    /* player already there with a higher score */
5092 #endif
5093
5094   }
5095
5096   if (position >= 0) 
5097     SaveScore(level_nr);
5098
5099   return position;
5100 }
5101
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5103 {
5104   int element = Feld[x][y];
5105   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5107   int horiz_move = (dx != 0);
5108   int sign = (horiz_move ? dx : dy);
5109   int step = sign * element_info[element].move_stepsize;
5110
5111   /* special values for move stepsize for spring and things on conveyor belt */
5112   if (horiz_move)
5113   {
5114     if (CAN_FALL(element) &&
5115         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117     else if (element == EL_SPRING)
5118       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5119   }
5120
5121   return step;
5122 }
5123
5124 inline static int getElementMoveStepsize(int x, int y)
5125 {
5126   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5127 }
5128
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5130 {
5131   if (player->GfxAction != action || player->GfxDir != dir)
5132   {
5133 #if 0
5134     printf("Player frame reset! (%d => %d, %d => %d)\n",
5135            player->GfxAction, action, player->GfxDir, dir);
5136 #endif
5137
5138     player->GfxAction = action;
5139     player->GfxDir = dir;
5140     player->Frame = 0;
5141     player->StepFrame = 0;
5142   }
5143 }
5144
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5147 {
5148   int element = Feld[x][y];
5149   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150   int last_gfx_frame = GfxFrame[x][y];
5151
5152   if (graphic_info[graphic].anim_global_sync)
5153     GfxFrame[x][y] = FrameCounter;
5154   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155     GfxFrame[x][y] = CustomValue[x][y];
5156   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157     GfxFrame[x][y] = element_info[element].collect_score;
5158   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159     GfxFrame[x][y] = ChangeDelay[x][y];
5160
5161   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162     DrawLevelGraphicAnimation(x, y, graphic);
5163 }
5164 #endif
5165
5166 static void ResetGfxAnimation(int x, int y)
5167 {
5168   GfxAction[x][y] = ACTION_DEFAULT;
5169   GfxDir[x][y] = MovDir[x][y];
5170   GfxFrame[x][y] = 0;
5171
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173   ResetGfxFrame(x, y, FALSE);
5174 #endif
5175 }
5176
5177 static void ResetRandomAnimationValue(int x, int y)
5178 {
5179   GfxRandom[x][y] = INIT_GFX_RANDOM();
5180 }
5181
5182 void InitMovingField(int x, int y, int direction)
5183 {
5184   int element = Feld[x][y];
5185   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5187   int newx = x + dx;
5188   int newy = y + dy;
5189   boolean is_moving_before, is_moving_after;
5190 #if 0
5191   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5192 #endif
5193
5194   /* check if element was/is moving or being moved before/after mode change */
5195 #if 1
5196 #if 1
5197   is_moving_before = (WasJustMoving[x][y] != 0);
5198 #else
5199   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200   is_moving_before = WasJustMoving[x][y];
5201 #endif
5202 #else
5203   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5204 #endif
5205   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5206
5207   /* reset animation only for moving elements which change direction of moving
5208      or which just started or stopped moving
5209      (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5211 #if 1
5212   if (is_moving_before != is_moving_after ||
5213       direction != MovDir[x][y])
5214     ResetGfxAnimation(x, y);
5215 #else
5216   if ((is_moving_before || is_moving_after) && !continues_moving)
5217     ResetGfxAnimation(x, y);
5218 #endif
5219 #else
5220   if (!continues_moving)
5221     ResetGfxAnimation(x, y);
5222 #endif
5223
5224   MovDir[x][y] = direction;
5225   GfxDir[x][y] = direction;
5226
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229                      direction == MV_DOWN && CAN_FALL(element) ?
5230                      ACTION_FALLING : ACTION_MOVING);
5231 #else
5232   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233                      ACTION_FALLING : ACTION_MOVING);
5234 #endif
5235
5236   /* this is needed for CEs with property "can move" / "not moving" */
5237
5238   if (is_moving_after)
5239   {
5240     if (Feld[newx][newy] == EL_EMPTY)
5241       Feld[newx][newy] = EL_BLOCKED;
5242
5243     MovDir[newx][newy] = MovDir[x][y];
5244
5245 #if USE_NEW_CUSTOM_VALUE
5246     CustomValue[newx][newy] = CustomValue[x][y];
5247 #endif
5248
5249     GfxFrame[newx][newy] = GfxFrame[x][y];
5250     GfxRandom[newx][newy] = GfxRandom[x][y];
5251     GfxAction[newx][newy] = GfxAction[x][y];
5252     GfxDir[newx][newy] = GfxDir[x][y];
5253   }
5254 }
5255
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5257 {
5258   int direction = MovDir[x][y];
5259   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5261
5262   *goes_to_x = newx;
5263   *goes_to_y = newy;
5264 }
5265
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5267 {
5268   int oldx = x, oldy = y;
5269   int direction = MovDir[x][y];
5270
5271   if (direction == MV_LEFT)
5272     oldx++;
5273   else if (direction == MV_RIGHT)
5274     oldx--;
5275   else if (direction == MV_UP)
5276     oldy++;
5277   else if (direction == MV_DOWN)
5278     oldy--;
5279
5280   *comes_from_x = oldx;
5281   *comes_from_y = oldy;
5282 }
5283
5284 int MovingOrBlocked2Element(int x, int y)
5285 {
5286   int element = Feld[x][y];
5287
5288   if (element == EL_BLOCKED)
5289   {
5290     int oldx, oldy;
5291
5292     Blocked2Moving(x, y, &oldx, &oldy);
5293     return Feld[oldx][oldy];
5294   }
5295   else
5296     return element;
5297 }
5298
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5300 {
5301   /* like MovingOrBlocked2Element(), but if element is moving
5302      and (x,y) is the field the moving element is just leaving,
5303      return EL_BLOCKED instead of the element value */
5304   int element = Feld[x][y];
5305
5306   if (IS_MOVING(x, y))
5307   {
5308     if (element == EL_BLOCKED)
5309     {
5310       int oldx, oldy;
5311
5312       Blocked2Moving(x, y, &oldx, &oldy);
5313       return Feld[oldx][oldy];
5314     }
5315     else
5316       return EL_BLOCKED;
5317   }
5318   else
5319     return element;
5320 }
5321
5322 static void RemoveField(int x, int y)
5323 {
5324   Feld[x][y] = EL_EMPTY;
5325
5326   MovPos[x][y] = 0;
5327   MovDir[x][y] = 0;
5328   MovDelay[x][y] = 0;
5329
5330 #if USE_NEW_CUSTOM_VALUE
5331   CustomValue[x][y] = 0;
5332 #endif
5333
5334   AmoebaNr[x][y] = 0;
5335   ChangeDelay[x][y] = 0;
5336   ChangePage[x][y] = -1;
5337   Pushed[x][y] = FALSE;
5338
5339 #if 0
5340   ExplodeField[x][y] = EX_TYPE_NONE;
5341 #endif
5342
5343   GfxElement[x][y] = EL_UNDEFINED;
5344   GfxAction[x][y] = ACTION_DEFAULT;
5345   GfxDir[x][y] = MV_NONE;
5346 #if 0
5347   /* !!! this would prevent the removed tile from being redrawn !!! */
5348   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5349 #endif
5350 }
5351
5352 void RemoveMovingField(int x, int y)
5353 {
5354   int oldx = x, oldy = y, newx = x, newy = y;
5355   int element = Feld[x][y];
5356   int next_element = EL_UNDEFINED;
5357
5358   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5359     return;
5360
5361   if (IS_MOVING(x, y))
5362   {
5363     Moving2Blocked(x, y, &newx, &newy);
5364
5365     if (Feld[newx][newy] != EL_BLOCKED)
5366     {
5367       /* element is moving, but target field is not free (blocked), but
5368          already occupied by something different (example: acid pool);
5369          in this case, only remove the moving field, but not the target */
5370
5371       RemoveField(oldx, oldy);
5372
5373       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5374
5375       TEST_DrawLevelField(oldx, oldy);
5376
5377       return;
5378     }
5379   }
5380   else if (element == EL_BLOCKED)
5381   {
5382     Blocked2Moving(x, y, &oldx, &oldy);
5383     if (!IS_MOVING(oldx, oldy))
5384       return;
5385   }
5386
5387   if (element == EL_BLOCKED &&
5388       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394     next_element = get_next_element(Feld[oldx][oldy]);
5395
5396   RemoveField(oldx, oldy);
5397   RemoveField(newx, newy);
5398
5399   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401   if (next_element != EL_UNDEFINED)
5402     Feld[oldx][oldy] = next_element;
5403
5404   TEST_DrawLevelField(oldx, oldy);
5405   TEST_DrawLevelField(newx, newy);
5406 }
5407
5408 void DrawDynamite(int x, int y)
5409 {
5410   int sx = SCREENX(x), sy = SCREENY(y);
5411   int graphic = el2img(Feld[x][y]);
5412   int frame;
5413
5414   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5415     return;
5416
5417   if (IS_WALKABLE_INSIDE(Back[x][y]))
5418     return;
5419
5420   if (Back[x][y])
5421     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422   else if (Store[x][y])
5423     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5424
5425   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5426
5427   if (Back[x][y] || Store[x][y])
5428     DrawGraphicThruMask(sx, sy, graphic, frame);
5429   else
5430     DrawGraphic(sx, sy, graphic, frame);
5431 }
5432
5433 void CheckDynamite(int x, int y)
5434 {
5435   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5436   {
5437     MovDelay[x][y]--;
5438
5439     if (MovDelay[x][y] != 0)
5440     {
5441       DrawDynamite(x, y);
5442       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5443
5444       return;
5445     }
5446   }
5447
5448   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5449
5450   Bang(x, y);
5451 }
5452
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5454 {
5455   boolean num_checked_players = 0;
5456   int i;
5457
5458   for (i = 0; i < MAX_PLAYERS; i++)
5459   {
5460     if (stored_player[i].active)
5461     {
5462       int sx = stored_player[i].jx;
5463       int sy = stored_player[i].jy;
5464
5465       if (num_checked_players == 0)
5466       {
5467         *sx1 = *sx2 = sx;
5468         *sy1 = *sy2 = sy;
5469       }
5470       else
5471       {
5472         *sx1 = MIN(*sx1, sx);
5473         *sy1 = MIN(*sy1, sy);
5474         *sx2 = MAX(*sx2, sx);
5475         *sy2 = MAX(*sy2, sy);
5476       }
5477
5478       num_checked_players++;
5479     }
5480   }
5481 }
5482
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5484 {
5485   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5486
5487   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5488
5489   return (sx2 - sx1 < SCR_FIELDX &&
5490           sy2 - sy1 < SCR_FIELDY);
5491 }
5492
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5494 {
5495   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5496
5497   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5498
5499   *sx = (sx1 + sx2) / 2;
5500   *sy = (sy1 + sy2) / 2;
5501 }
5502
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504                         boolean center_screen, boolean quick_relocation)
5505 {
5506   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507   boolean no_delay = (tape.warp_forward);
5508   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5510
5511   if (quick_relocation)
5512   {
5513     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5514     {
5515       if (!level.shifted_relocation || center_screen)
5516       {
5517         /* quick relocation (without scrolling), with centering of screen */
5518
5519         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5520                     x > SBX_Right + MIDPOSX ? SBX_Right :
5521                     x - MIDPOSX);
5522
5523         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5525                     y - MIDPOSY);
5526       }
5527       else
5528       {
5529         /* quick relocation (without scrolling), but do not center screen */
5530
5531         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5532                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5533                                old_x - MIDPOSX);
5534
5535         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5537                                old_y - MIDPOSY);
5538
5539         int offset_x = x + (scroll_x - center_scroll_x);
5540         int offset_y = y + (scroll_y - center_scroll_y);
5541
5542         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5543                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544                     offset_x - MIDPOSX);
5545
5546         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548                     offset_y - MIDPOSY);
5549       }
5550     }
5551     else
5552     {
5553 #if 1
5554       if (!level.shifted_relocation || center_screen)
5555       {
5556         /* quick relocation (without scrolling), with centering of screen */
5557
5558         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5559                     x > SBX_Right + MIDPOSX ? SBX_Right :
5560                     x - MIDPOSX);
5561
5562         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564                     y - MIDPOSY);
5565       }
5566       else
5567       {
5568         /* quick relocation (without scrolling), but do not center screen */
5569
5570         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5571                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5572                                old_x - MIDPOSX);
5573
5574         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5576                                old_y - MIDPOSY);
5577
5578         int offset_x = x + (scroll_x - center_scroll_x);
5579         int offset_y = y + (scroll_y - center_scroll_y);
5580
5581         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5582                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583                     offset_x - MIDPOSX);
5584
5585         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587                     offset_y - MIDPOSY);
5588       }
5589 #else
5590       /* quick relocation (without scrolling), inside visible screen area */
5591
5592       int offset = game.scroll_delay_value;
5593
5594       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5595           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5597
5598       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5599           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5601
5602       /* don't scroll over playfield boundaries */
5603       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5605
5606       /* don't scroll over playfield boundaries */
5607       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5609 #endif
5610     }
5611
5612     RedrawPlayfield(TRUE, 0,0,0,0);
5613   }
5614   else
5615   {
5616 #if 1
5617     int scroll_xx, scroll_yy;
5618
5619     if (!level.shifted_relocation || center_screen)
5620     {
5621       /* visible relocation (with scrolling), with centering of screen */
5622
5623       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5624                    x > SBX_Right + MIDPOSX ? SBX_Right :
5625                    x - MIDPOSX);
5626
5627       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5629                    y - MIDPOSY);
5630     }
5631     else
5632     {
5633       /* visible relocation (with scrolling), but do not center screen */
5634
5635       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5636                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5637                              old_x - MIDPOSX);
5638
5639       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5641                              old_y - MIDPOSY);
5642
5643       int offset_x = x + (scroll_x - center_scroll_x);
5644       int offset_y = y + (scroll_y - center_scroll_y);
5645
5646       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5647                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648                    offset_x - MIDPOSX);
5649
5650       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652                    offset_y - MIDPOSY);
5653     }
5654
5655 #else
5656
5657     /* visible relocation (with scrolling), with centering of screen */
5658
5659     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5660                      x > SBX_Right + MIDPOSX ? SBX_Right :
5661                      x - MIDPOSX);
5662
5663     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665                      y - MIDPOSY);
5666 #endif
5667
5668     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5669
5670     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5671     {
5672       int dx = 0, dy = 0;
5673       int fx = FX, fy = FY;
5674
5675       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5677
5678       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5679         break;
5680
5681       scroll_x -= dx;
5682       scroll_y -= dy;
5683
5684       fx += dx * TILEX / 2;
5685       fy += dy * TILEY / 2;
5686
5687       ScrollLevel(dx, dy);
5688       DrawAllPlayers();
5689
5690       /* scroll in two steps of half tile size to make things smoother */
5691       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5692       FlushDisplay();
5693       Delay(wait_delay_value);
5694
5695       /* scroll second step to align at full tile size */
5696       BackToFront();
5697       Delay(wait_delay_value);
5698     }
5699
5700     DrawAllPlayers();
5701     BackToFront();
5702     Delay(wait_delay_value);
5703   }
5704 }
5705
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5707 {
5708   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709   int player_nr = GET_PLAYER_NR(el_player);
5710   struct PlayerInfo *player = &stored_player[player_nr];
5711   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712   boolean no_delay = (tape.warp_forward);
5713   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715   int old_jx = player->jx;
5716   int old_jy = player->jy;
5717   int old_element = Feld[old_jx][old_jy];
5718   int element = Feld[jx][jy];
5719   boolean player_relocated = (old_jx != jx || old_jy != jy);
5720
5721   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5723   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5725   int leave_side_horiz = move_dir_horiz;
5726   int leave_side_vert  = move_dir_vert;
5727   int enter_side = enter_side_horiz | enter_side_vert;
5728   int leave_side = leave_side_horiz | leave_side_vert;
5729
5730   if (player->GameOver)         /* do not reanimate dead player */
5731     return;
5732
5733   if (!player_relocated)        /* no need to relocate the player */
5734     return;
5735
5736   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5737   {
5738     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5739     DrawLevelField(jx, jy);
5740   }
5741
5742   if (player->present)
5743   {
5744     while (player->MovPos)
5745     {
5746       ScrollPlayer(player, SCROLL_GO_ON);
5747       ScrollScreen(NULL, SCROLL_GO_ON);
5748
5749       AdvanceFrameAndPlayerCounters(player->index_nr);
5750
5751       DrawPlayer(player);
5752
5753       BackToFront();
5754       Delay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         /* needed here only to cleanup last field */
5758     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Feld[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5776      possible that the relocation target field did not contain a player element,
5777      but a walkable element, to which the new player was relocated -- in this
5778      case, restore that (already initialized!) element on the player field */
5779   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5780   {
5781     Feld[jx][jy] = element;     /* restore previously existing element */
5782 #if 0
5783     /* !!! do not initialize already initialized element a second time !!! */
5784     /* (this causes at least problems with "element creation" CE trigger for
5785        already existing elements, and existing Sokoban fields counted twice) */
5786     InitField(jx, jy, FALSE);
5787 #endif
5788   }
5789
5790   /* only visually relocate centered player */
5791   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5792                      FALSE, level.instant_relocation);
5793
5794   TestIfPlayerTouchesBadThing(jx, jy);
5795   TestIfPlayerTouchesCustomElement(jx, jy);
5796
5797   if (IS_CUSTOM_ELEMENT(element))
5798     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5799                                player->index_bit, enter_side);
5800
5801   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5802                                       player->index_bit, enter_side);
5803
5804 #if 1
5805   if (player->is_switching)
5806   {
5807     /* ensure that relocation while still switching an element does not cause
5808        a new element to be treated as also switched directly after relocation
5809        (this is important for teleporter switches that teleport the player to
5810        a place where another teleporter switch is in the same direction, which
5811        would then incorrectly be treated as immediately switched before the
5812        direction key that caused the switch was released) */
5813
5814     player->switch_x += jx - old_jx;
5815     player->switch_y += jy - old_jy;
5816   }
5817 #endif
5818 }
5819
5820 void Explode(int ex, int ey, int phase, int mode)
5821 {
5822   int x, y;
5823   int last_phase;
5824   int border_element;
5825
5826   /* !!! eliminate this variable !!! */
5827   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5828
5829   if (game.explosions_delayed)
5830   {
5831     ExplodeField[ex][ey] = mode;
5832     return;
5833   }
5834
5835   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5836   {
5837     int center_element = Feld[ex][ey];
5838     int artwork_element, explosion_element;     /* set these values later */
5839
5840 #if 0
5841     /* --- This is only really needed (and now handled) in "Impact()". --- */
5842     /* do not explode moving elements that left the explode field in time */
5843     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5844         center_element == EL_EMPTY &&
5845         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5846       return;
5847 #endif
5848
5849 #if 0
5850     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5851     if (mode == EX_TYPE_NORMAL ||
5852         mode == EX_TYPE_CENTER ||
5853         mode == EX_TYPE_CROSS)
5854       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5855 #endif
5856
5857     /* remove things displayed in background while burning dynamite */
5858     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5859       Back[ex][ey] = 0;
5860
5861     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5862     {
5863       /* put moving element to center field (and let it explode there) */
5864       center_element = MovingOrBlocked2Element(ex, ey);
5865       RemoveMovingField(ex, ey);
5866       Feld[ex][ey] = center_element;
5867     }
5868
5869     /* now "center_element" is finally determined -- set related values now */
5870     artwork_element = center_element;           /* for custom player artwork */
5871     explosion_element = center_element;         /* for custom player artwork */
5872
5873     if (IS_PLAYER(ex, ey))
5874     {
5875       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5876
5877       artwork_element = stored_player[player_nr].artwork_element;
5878
5879       if (level.use_explosion_element[player_nr])
5880       {
5881         explosion_element = level.explosion_element[player_nr];
5882         artwork_element = explosion_element;
5883       }
5884     }
5885
5886 #if 1
5887     if (mode == EX_TYPE_NORMAL ||
5888         mode == EX_TYPE_CENTER ||
5889         mode == EX_TYPE_CROSS)
5890       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5891 #endif
5892
5893     last_phase = element_info[explosion_element].explosion_delay + 1;
5894
5895     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5896     {
5897       int xx = x - ex + 1;
5898       int yy = y - ey + 1;
5899       int element;
5900
5901       if (!IN_LEV_FIELD(x, y) ||
5902           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5903           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5904         continue;
5905
5906       element = Feld[x][y];
5907
5908       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5909       {
5910         element = MovingOrBlocked2Element(x, y);
5911
5912         if (!IS_EXPLOSION_PROOF(element))
5913           RemoveMovingField(x, y);
5914       }
5915
5916       /* indestructible elements can only explode in center (but not flames) */
5917       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5918                                            mode == EX_TYPE_BORDER)) ||
5919           element == EL_FLAMES)
5920         continue;
5921
5922       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5923          behaviour, for example when touching a yamyam that explodes to rocks
5924          with active deadly shield, a rock is created under the player !!! */
5925       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5926 #if 0
5927       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5928           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5929            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5930 #else
5931       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5932 #endif
5933       {
5934         if (IS_ACTIVE_BOMB(element))
5935         {
5936           /* re-activate things under the bomb like gate or penguin */
5937           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5938           Back[x][y] = 0;
5939         }
5940
5941         continue;
5942       }
5943
5944       /* save walkable background elements while explosion on same tile */
5945       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5946           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5947         Back[x][y] = element;
5948
5949       /* ignite explodable elements reached by other explosion */
5950       if (element == EL_EXPLOSION)
5951         element = Store2[x][y];
5952
5953       if (AmoebaNr[x][y] &&
5954           (element == EL_AMOEBA_FULL ||
5955            element == EL_BD_AMOEBA ||
5956            element == EL_AMOEBA_GROWING))
5957       {
5958         AmoebaCnt[AmoebaNr[x][y]]--;
5959         AmoebaCnt2[AmoebaNr[x][y]]--;
5960       }
5961
5962       RemoveField(x, y);
5963
5964       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5965       {
5966         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5967
5968         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5969
5970         if (PLAYERINFO(ex, ey)->use_murphy)
5971           Store[x][y] = EL_EMPTY;
5972       }
5973
5974       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5975          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5976       else if (ELEM_IS_PLAYER(center_element))
5977         Store[x][y] = EL_EMPTY;
5978       else if (center_element == EL_YAMYAM)
5979         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5980       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5981         Store[x][y] = element_info[center_element].content.e[xx][yy];
5982 #if 1
5983       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5984          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5985          otherwise) -- FIX THIS !!! */
5986       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5987         Store[x][y] = element_info[element].content.e[1][1];
5988 #else
5989       else if (!CAN_EXPLODE(element))
5990         Store[x][y] = element_info[element].content.e[1][1];
5991 #endif
5992       else
5993         Store[x][y] = EL_EMPTY;
5994
5995       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5996           center_element == EL_AMOEBA_TO_DIAMOND)
5997         Store2[x][y] = element;
5998
5999       Feld[x][y] = EL_EXPLOSION;
6000       GfxElement[x][y] = artwork_element;
6001
6002       ExplodePhase[x][y] = 1;
6003       ExplodeDelay[x][y] = last_phase;
6004
6005       Stop[x][y] = TRUE;
6006     }
6007
6008     if (center_element == EL_YAMYAM)
6009       game.yamyam_content_nr =
6010         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6011
6012     return;
6013   }
6014
6015   if (Stop[ex][ey])
6016     return;
6017
6018   x = ex;
6019   y = ey;
6020
6021   if (phase == 1)
6022     GfxFrame[x][y] = 0;         /* restart explosion animation */
6023
6024   last_phase = ExplodeDelay[x][y];
6025
6026   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6027
6028 #ifdef DEBUG
6029
6030   /* activate this even in non-DEBUG version until cause for crash in
6031      getGraphicAnimationFrame() (see below) is found and eliminated */
6032
6033 #endif
6034 #if 1
6035
6036 #if 1
6037   /* this can happen if the player leaves an explosion just in time */
6038   if (GfxElement[x][y] == EL_UNDEFINED)
6039     GfxElement[x][y] = EL_EMPTY;
6040 #else
6041   if (GfxElement[x][y] == EL_UNDEFINED)
6042   {
6043     printf("\n\n");
6044     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6045     printf("Explode(): This should never happen!\n");
6046     printf("\n\n");
6047
6048     GfxElement[x][y] = EL_EMPTY;
6049   }
6050 #endif
6051
6052 #endif
6053
6054   border_element = Store2[x][y];
6055   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     border_element = StorePlayer[x][y];
6057
6058   if (phase == element_info[border_element].ignition_delay ||
6059       phase == last_phase)
6060   {
6061     boolean border_explosion = FALSE;
6062
6063     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6064         !PLAYER_EXPLOSION_PROTECTED(x, y))
6065     {
6066       KillPlayerUnlessExplosionProtected(x, y);
6067       border_explosion = TRUE;
6068     }
6069     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6070     {
6071       Feld[x][y] = Store2[x][y];
6072       Store2[x][y] = 0;
6073       Bang(x, y);
6074       border_explosion = TRUE;
6075     }
6076     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6077     {
6078       AmoebeUmwandeln(x, y);
6079       Store2[x][y] = 0;
6080       border_explosion = TRUE;
6081     }
6082
6083     /* if an element just explodes due to another explosion (chain-reaction),
6084        do not immediately end the new explosion when it was the last frame of
6085        the explosion (as it would be done in the following "if"-statement!) */
6086     if (border_explosion && phase == last_phase)
6087       return;
6088   }
6089
6090   if (phase == last_phase)
6091   {
6092     int element;
6093
6094     element = Feld[x][y] = Store[x][y];
6095     Store[x][y] = Store2[x][y] = 0;
6096     GfxElement[x][y] = EL_UNDEFINED;
6097
6098     /* player can escape from explosions and might therefore be still alive */
6099     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6100         element <= EL_PLAYER_IS_EXPLODING_4)
6101     {
6102       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6103       int explosion_element = EL_PLAYER_1 + player_nr;
6104       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6105       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6106
6107       if (level.use_explosion_element[player_nr])
6108         explosion_element = level.explosion_element[player_nr];
6109
6110       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6111                     element_info[explosion_element].content.e[xx][yy]);
6112     }
6113
6114     /* restore probably existing indestructible background element */
6115     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6116       element = Feld[x][y] = Back[x][y];
6117     Back[x][y] = 0;
6118
6119     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6120     GfxDir[x][y] = MV_NONE;
6121     ChangeDelay[x][y] = 0;
6122     ChangePage[x][y] = -1;
6123
6124 #if USE_NEW_CUSTOM_VALUE
6125     CustomValue[x][y] = 0;
6126 #endif
6127
6128     InitField_WithBug2(x, y, FALSE);
6129
6130     TEST_DrawLevelField(x, y);
6131
6132     TestIfElementTouchesCustomElement(x, y);
6133
6134     if (GFX_CRUMBLED(element))
6135       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6136
6137     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6138       StorePlayer[x][y] = 0;
6139
6140     if (ELEM_IS_PLAYER(element))
6141       RelocatePlayer(x, y, element);
6142   }
6143   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6144   {
6145     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6146     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6147
6148     if (phase == delay)
6149       TEST_DrawLevelFieldCrumbledSand(x, y);
6150
6151     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6152     {
6153       DrawLevelElement(x, y, Back[x][y]);
6154       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6155     }
6156     else if (IS_WALKABLE_UNDER(Back[x][y]))
6157     {
6158       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6159       DrawLevelElementThruMask(x, y, Back[x][y]);
6160     }
6161     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6162       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6163   }
6164 }
6165
6166 void DynaExplode(int ex, int ey)
6167 {
6168   int i, j;
6169   int dynabomb_element = Feld[ex][ey];
6170   int dynabomb_size = 1;
6171   boolean dynabomb_xl = FALSE;
6172   struct PlayerInfo *player;
6173   static int xy[4][2] =
6174   {
6175     { 0, -1 },
6176     { -1, 0 },
6177     { +1, 0 },
6178     { 0, +1 }
6179   };
6180
6181   if (IS_ACTIVE_BOMB(dynabomb_element))
6182   {
6183     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6184     dynabomb_size = player->dynabomb_size;
6185     dynabomb_xl = player->dynabomb_xl;
6186     player->dynabombs_left++;
6187   }
6188
6189   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6190
6191   for (i = 0; i < NUM_DIRECTIONS; i++)
6192   {
6193     for (j = 1; j <= dynabomb_size; j++)
6194     {
6195       int x = ex + j * xy[i][0];
6196       int y = ey + j * xy[i][1];
6197       int element;
6198
6199       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6200         break;
6201
6202       element = Feld[x][y];
6203
6204       /* do not restart explosions of fields with active bombs */
6205       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6206         continue;
6207
6208       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6209
6210       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6211           !IS_DIGGABLE(element) && !dynabomb_xl)
6212         break;
6213     }
6214   }
6215 }
6216
6217 void Bang(int x, int y)
6218 {
6219   int element = MovingOrBlocked2Element(x, y);
6220   int explosion_type = EX_TYPE_NORMAL;
6221
6222   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6223   {
6224     struct PlayerInfo *player = PLAYERINFO(x, y);
6225
6226 #if USE_FIX_CE_ACTION_WITH_PLAYER
6227     element = Feld[x][y] = player->initial_element;
6228 #else
6229     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6230                             player->element_nr);
6231 #endif
6232
6233     if (level.use_explosion_element[player->index_nr])
6234     {
6235       int explosion_element = level.explosion_element[player->index_nr];
6236
6237       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6238         explosion_type = EX_TYPE_CROSS;
6239       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6240         explosion_type = EX_TYPE_CENTER;
6241     }
6242   }
6243
6244   switch (element)
6245   {
6246     case EL_BUG:
6247     case EL_SPACESHIP:
6248     case EL_BD_BUTTERFLY:
6249     case EL_BD_FIREFLY:
6250     case EL_YAMYAM:
6251     case EL_DARK_YAMYAM:
6252     case EL_ROBOT:
6253     case EL_PACMAN:
6254     case EL_MOLE:
6255       RaiseScoreElement(element);
6256       break;
6257
6258     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6259     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6260     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6261     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6262     case EL_DYNABOMB_INCREASE_NUMBER:
6263     case EL_DYNABOMB_INCREASE_SIZE:
6264     case EL_DYNABOMB_INCREASE_POWER:
6265       explosion_type = EX_TYPE_DYNA;
6266       break;
6267
6268     case EL_DC_LANDMINE:
6269 #if 0
6270     case EL_EM_EXIT_OPEN:
6271     case EL_EM_STEEL_EXIT_OPEN:
6272 #endif
6273       explosion_type = EX_TYPE_CENTER;
6274       break;
6275
6276     case EL_PENGUIN:
6277     case EL_LAMP:
6278     case EL_LAMP_ACTIVE:
6279     case EL_AMOEBA_TO_DIAMOND:
6280       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6281         explosion_type = EX_TYPE_CENTER;
6282       break;
6283
6284     default:
6285       if (element_info[element].explosion_type == EXPLODES_CROSS)
6286         explosion_type = EX_TYPE_CROSS;
6287       else if (element_info[element].explosion_type == EXPLODES_1X1)
6288         explosion_type = EX_TYPE_CENTER;
6289       break;
6290   }
6291
6292   if (explosion_type == EX_TYPE_DYNA)
6293     DynaExplode(x, y);
6294   else
6295     Explode(x, y, EX_PHASE_START, explosion_type);
6296
6297   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6298 }
6299
6300 void SplashAcid(int x, int y)
6301 {
6302   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6303       (!IN_LEV_FIELD(x - 1, y - 2) ||
6304        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6305     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6306
6307   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6308       (!IN_LEV_FIELD(x + 1, y - 2) ||
6309        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6310     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6311
6312   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6313 }
6314
6315 static void InitBeltMovement()
6316 {
6317   static int belt_base_element[4] =
6318   {
6319     EL_CONVEYOR_BELT_1_LEFT,
6320     EL_CONVEYOR_BELT_2_LEFT,
6321     EL_CONVEYOR_BELT_3_LEFT,
6322     EL_CONVEYOR_BELT_4_LEFT
6323   };
6324   static int belt_base_active_element[4] =
6325   {
6326     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6327     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6328     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6329     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6330   };
6331
6332   int x, y, i, j;
6333
6334   /* set frame order for belt animation graphic according to belt direction */
6335   for (i = 0; i < NUM_BELTS; i++)
6336   {
6337     int belt_nr = i;
6338
6339     for (j = 0; j < NUM_BELT_PARTS; j++)
6340     {
6341       int element = belt_base_active_element[belt_nr] + j;
6342       int graphic_1 = el2img(element);
6343       int graphic_2 = el2panelimg(element);
6344
6345       if (game.belt_dir[i] == MV_LEFT)
6346       {
6347         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6348         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6349       }
6350       else
6351       {
6352         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6353         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6354       }
6355     }
6356   }
6357
6358   SCAN_PLAYFIELD(x, y)
6359   {
6360     int element = Feld[x][y];
6361
6362     for (i = 0; i < NUM_BELTS; i++)
6363     {
6364       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6365       {
6366         int e_belt_nr = getBeltNrFromBeltElement(element);
6367         int belt_nr = i;
6368
6369         if (e_belt_nr == belt_nr)
6370         {
6371           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6372
6373           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6374         }
6375       }
6376     }
6377   }
6378 }
6379
6380 static void ToggleBeltSwitch(int x, int y)
6381 {
6382   static int belt_base_element[4] =
6383   {
6384     EL_CONVEYOR_BELT_1_LEFT,
6385     EL_CONVEYOR_BELT_2_LEFT,
6386     EL_CONVEYOR_BELT_3_LEFT,
6387     EL_CONVEYOR_BELT_4_LEFT
6388   };
6389   static int belt_base_active_element[4] =
6390   {
6391     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6392     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6393     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6394     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6395   };
6396   static int belt_base_switch_element[4] =
6397   {
6398     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6399     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6400     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6401     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6402   };
6403   static int belt_move_dir[4] =
6404   {
6405     MV_LEFT,
6406     MV_NONE,
6407     MV_RIGHT,
6408     MV_NONE,
6409   };
6410
6411   int element = Feld[x][y];
6412   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6413   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6414   int belt_dir = belt_move_dir[belt_dir_nr];
6415   int xx, yy, i;
6416
6417   if (!IS_BELT_SWITCH(element))
6418     return;
6419
6420   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6421   game.belt_dir[belt_nr] = belt_dir;
6422
6423   if (belt_dir_nr == 3)
6424     belt_dir_nr = 1;
6425
6426   /* set frame order for belt animation graphic according to belt direction */
6427   for (i = 0; i < NUM_BELT_PARTS; i++)
6428   {
6429     int element = belt_base_active_element[belt_nr] + i;
6430     int graphic_1 = el2img(element);
6431     int graphic_2 = el2panelimg(element);
6432
6433     if (belt_dir == MV_LEFT)
6434     {
6435       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6436       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6437     }
6438     else
6439     {
6440       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6441       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6442     }
6443   }
6444
6445   SCAN_PLAYFIELD(xx, yy)
6446   {
6447     int element = Feld[xx][yy];
6448
6449     if (IS_BELT_SWITCH(element))
6450     {
6451       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6452
6453       if (e_belt_nr == belt_nr)
6454       {
6455         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6456         TEST_DrawLevelField(xx, yy);
6457       }
6458     }
6459     else if (IS_BELT(element) && belt_dir != MV_NONE)
6460     {
6461       int e_belt_nr = getBeltNrFromBeltElement(element);
6462
6463       if (e_belt_nr == belt_nr)
6464       {
6465         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6466
6467         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6468         TEST_DrawLevelField(xx, yy);
6469       }
6470     }
6471     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6472     {
6473       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6474
6475       if (e_belt_nr == belt_nr)
6476       {
6477         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6478
6479         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6480         TEST_DrawLevelField(xx, yy);
6481       }
6482     }
6483   }
6484 }
6485
6486 static void ToggleSwitchgateSwitch(int x, int y)
6487 {
6488   int xx, yy;
6489
6490   game.switchgate_pos = !game.switchgate_pos;
6491
6492   SCAN_PLAYFIELD(xx, yy)
6493   {
6494     int element = Feld[xx][yy];
6495
6496 #if !USE_BOTH_SWITCHGATE_SWITCHES
6497     if (element == EL_SWITCHGATE_SWITCH_UP ||
6498         element == EL_SWITCHGATE_SWITCH_DOWN)
6499     {
6500       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6501       TEST_DrawLevelField(xx, yy);
6502     }
6503     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6504              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6505     {
6506       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6507       TEST_DrawLevelField(xx, yy);
6508     }
6509 #else
6510     if (element == EL_SWITCHGATE_SWITCH_UP)
6511     {
6512       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6513       TEST_DrawLevelField(xx, yy);
6514     }
6515     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6516     {
6517       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6518       TEST_DrawLevelField(xx, yy);
6519     }
6520     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6521     {
6522       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6523       TEST_DrawLevelField(xx, yy);
6524     }
6525     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6526     {
6527       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6528       TEST_DrawLevelField(xx, yy);
6529     }
6530 #endif
6531     else if (element == EL_SWITCHGATE_OPEN ||
6532              element == EL_SWITCHGATE_OPENING)
6533     {
6534       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6535
6536       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6537     }
6538     else if (element == EL_SWITCHGATE_CLOSED ||
6539              element == EL_SWITCHGATE_CLOSING)
6540     {
6541       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6542
6543       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6544     }
6545   }
6546 }
6547
6548 static int getInvisibleActiveFromInvisibleElement(int element)
6549 {
6550   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6551           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6552           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6553           element);
6554 }
6555
6556 static int getInvisibleFromInvisibleActiveElement(int element)
6557 {
6558   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6559           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6560           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6561           element);
6562 }
6563
6564 static void RedrawAllLightSwitchesAndInvisibleElements()
6565 {
6566   int x, y;
6567
6568   SCAN_PLAYFIELD(x, y)
6569   {
6570     int element = Feld[x][y];
6571
6572     if (element == EL_LIGHT_SWITCH &&
6573         game.light_time_left > 0)
6574     {
6575       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6576       TEST_DrawLevelField(x, y);
6577     }
6578     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6579              game.light_time_left == 0)
6580     {
6581       Feld[x][y] = EL_LIGHT_SWITCH;
6582       TEST_DrawLevelField(x, y);
6583     }
6584     else if (element == EL_EMC_DRIPPER &&
6585              game.light_time_left > 0)
6586     {
6587       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6588       TEST_DrawLevelField(x, y);
6589     }
6590     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6591              game.light_time_left == 0)
6592     {
6593       Feld[x][y] = EL_EMC_DRIPPER;
6594       TEST_DrawLevelField(x, y);
6595     }
6596     else if (element == EL_INVISIBLE_STEELWALL ||
6597              element == EL_INVISIBLE_WALL ||
6598              element == EL_INVISIBLE_SAND)
6599     {
6600       if (game.light_time_left > 0)
6601         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6602
6603       TEST_DrawLevelField(x, y);
6604
6605       /* uncrumble neighbour fields, if needed */
6606       if (element == EL_INVISIBLE_SAND)
6607         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6608     }
6609     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6610              element == EL_INVISIBLE_WALL_ACTIVE ||
6611              element == EL_INVISIBLE_SAND_ACTIVE)
6612     {
6613       if (game.light_time_left == 0)
6614         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6615
6616       TEST_DrawLevelField(x, y);
6617
6618       /* re-crumble neighbour fields, if needed */
6619       if (element == EL_INVISIBLE_SAND)
6620         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6621     }
6622   }
6623 }
6624
6625 static void RedrawAllInvisibleElementsForLenses()
6626 {
6627   int x, y;
6628
6629   SCAN_PLAYFIELD(x, y)
6630   {
6631     int element = Feld[x][y];
6632
6633     if (element == EL_EMC_DRIPPER &&
6634         game.lenses_time_left > 0)
6635     {
6636       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6637       TEST_DrawLevelField(x, y);
6638     }
6639     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6640              game.lenses_time_left == 0)
6641     {
6642       Feld[x][y] = EL_EMC_DRIPPER;
6643       TEST_DrawLevelField(x, y);
6644     }
6645     else if (element == EL_INVISIBLE_STEELWALL ||
6646              element == EL_INVISIBLE_WALL ||
6647              element == EL_INVISIBLE_SAND)
6648     {
6649       if (game.lenses_time_left > 0)
6650         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6651
6652       TEST_DrawLevelField(x, y);
6653
6654       /* uncrumble neighbour fields, if needed */
6655       if (element == EL_INVISIBLE_SAND)
6656         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6657     }
6658     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6659              element == EL_INVISIBLE_WALL_ACTIVE ||
6660              element == EL_INVISIBLE_SAND_ACTIVE)
6661     {
6662       if (game.lenses_time_left == 0)
6663         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6664
6665       TEST_DrawLevelField(x, y);
6666
6667       /* re-crumble neighbour fields, if needed */
6668       if (element == EL_INVISIBLE_SAND)
6669         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6670     }
6671   }
6672 }
6673
6674 static void RedrawAllInvisibleElementsForMagnifier()
6675 {
6676   int x, y;
6677
6678   SCAN_PLAYFIELD(x, y)
6679   {
6680     int element = Feld[x][y];
6681
6682     if (element == EL_EMC_FAKE_GRASS &&
6683         game.magnify_time_left > 0)
6684     {
6685       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6686       TEST_DrawLevelField(x, y);
6687     }
6688     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6689              game.magnify_time_left == 0)
6690     {
6691       Feld[x][y] = EL_EMC_FAKE_GRASS;
6692       TEST_DrawLevelField(x, y);
6693     }
6694     else if (IS_GATE_GRAY(element) &&
6695              game.magnify_time_left > 0)
6696     {
6697       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6698                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6699                     IS_EM_GATE_GRAY(element) ?
6700                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6701                     IS_EMC_GATE_GRAY(element) ?
6702                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6703                     IS_DC_GATE_GRAY(element) ?
6704                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6705                     element);
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (IS_GATE_GRAY_ACTIVE(element) &&
6709              game.magnify_time_left == 0)
6710     {
6711       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6712                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6713                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6714                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6715                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6716                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6717                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6718                     EL_DC_GATE_WHITE_GRAY :
6719                     element);
6720       TEST_DrawLevelField(x, y);
6721     }
6722   }
6723 }
6724
6725 static void ToggleLightSwitch(int x, int y)
6726 {
6727   int element = Feld[x][y];
6728
6729   game.light_time_left =
6730     (element == EL_LIGHT_SWITCH ?
6731      level.time_light * FRAMES_PER_SECOND : 0);
6732
6733   RedrawAllLightSwitchesAndInvisibleElements();
6734 }
6735
6736 static void ActivateTimegateSwitch(int x, int y)
6737 {
6738   int xx, yy;
6739
6740   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6741
6742   SCAN_PLAYFIELD(xx, yy)
6743   {
6744     int element = Feld[xx][yy];
6745
6746     if (element == EL_TIMEGATE_CLOSED ||
6747         element == EL_TIMEGATE_CLOSING)
6748     {
6749       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6750       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6751     }
6752
6753     /*
6754     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6755     {
6756       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6757       TEST_DrawLevelField(xx, yy);
6758     }
6759     */
6760
6761   }
6762
6763 #if 1
6764   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6765                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6766 #else
6767   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6768 #endif
6769 }
6770
6771 void Impact(int x, int y)
6772 {
6773   boolean last_line = (y == lev_fieldy - 1);
6774   boolean object_hit = FALSE;
6775   boolean impact = (last_line || object_hit);
6776   int element = Feld[x][y];
6777   int smashed = EL_STEELWALL;
6778
6779   if (!last_line)       /* check if element below was hit */
6780   {
6781     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6782       return;
6783
6784     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6785                                          MovDir[x][y + 1] != MV_DOWN ||
6786                                          MovPos[x][y + 1] <= TILEY / 2));
6787
6788     /* do not smash moving elements that left the smashed field in time */
6789     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6790         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6791       object_hit = FALSE;
6792
6793 #if USE_QUICKSAND_IMPACT_BUGFIX
6794     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6795     {
6796       RemoveMovingField(x, y + 1);
6797       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6798       Feld[x][y + 2] = EL_ROCK;
6799       TEST_DrawLevelField(x, y + 2);
6800
6801       object_hit = TRUE;
6802     }
6803
6804     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6805     {
6806       RemoveMovingField(x, y + 1);
6807       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6808       Feld[x][y + 2] = EL_ROCK;
6809       TEST_DrawLevelField(x, y + 2);
6810
6811       object_hit = TRUE;
6812     }
6813 #endif
6814
6815     if (object_hit)
6816       smashed = MovingOrBlocked2Element(x, y + 1);
6817
6818     impact = (last_line || object_hit);
6819   }
6820
6821   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6822   {
6823     SplashAcid(x, y + 1);
6824     return;
6825   }
6826
6827   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6828   /* only reset graphic animation if graphic really changes after impact */
6829   if (impact &&
6830       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6831   {
6832     ResetGfxAnimation(x, y);
6833     TEST_DrawLevelField(x, y);
6834   }
6835
6836   if (impact && CAN_EXPLODE_IMPACT(element))
6837   {
6838     Bang(x, y);
6839     return;
6840   }
6841   else if (impact && element == EL_PEARL &&
6842            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6843   {
6844     ResetGfxAnimation(x, y);
6845
6846     Feld[x][y] = EL_PEARL_BREAKING;
6847     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6848     return;
6849   }
6850   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6851   {
6852     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6853
6854     return;
6855   }
6856
6857   if (impact && element == EL_AMOEBA_DROP)
6858   {
6859     if (object_hit && IS_PLAYER(x, y + 1))
6860       KillPlayerUnlessEnemyProtected(x, y + 1);
6861     else if (object_hit && smashed == EL_PENGUIN)
6862       Bang(x, y + 1);
6863     else
6864     {
6865       Feld[x][y] = EL_AMOEBA_GROWING;
6866       Store[x][y] = EL_AMOEBA_WET;
6867
6868       ResetRandomAnimationValue(x, y);
6869     }
6870     return;
6871   }
6872
6873   if (object_hit)               /* check which object was hit */
6874   {
6875     if ((CAN_PASS_MAGIC_WALL(element) && 
6876          (smashed == EL_MAGIC_WALL ||
6877           smashed == EL_BD_MAGIC_WALL)) ||
6878         (CAN_PASS_DC_MAGIC_WALL(element) &&
6879          smashed == EL_DC_MAGIC_WALL))
6880     {
6881       int xx, yy;
6882       int activated_magic_wall =
6883         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6884          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6885          EL_DC_MAGIC_WALL_ACTIVE);
6886
6887       /* activate magic wall / mill */
6888       SCAN_PLAYFIELD(xx, yy)
6889       {
6890         if (Feld[xx][yy] == smashed)
6891           Feld[xx][yy] = activated_magic_wall;
6892       }
6893
6894       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6895       game.magic_wall_active = TRUE;
6896
6897       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6898                             SND_MAGIC_WALL_ACTIVATING :
6899                             smashed == EL_BD_MAGIC_WALL ?
6900                             SND_BD_MAGIC_WALL_ACTIVATING :
6901                             SND_DC_MAGIC_WALL_ACTIVATING));
6902     }
6903
6904     if (IS_PLAYER(x, y + 1))
6905     {
6906       if (CAN_SMASH_PLAYER(element))
6907       {
6908         KillPlayerUnlessEnemyProtected(x, y + 1);
6909         return;
6910       }
6911     }
6912     else if (smashed == EL_PENGUIN)
6913     {
6914       if (CAN_SMASH_PLAYER(element))
6915       {
6916         Bang(x, y + 1);
6917         return;
6918       }
6919     }
6920     else if (element == EL_BD_DIAMOND)
6921     {
6922       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6923       {
6924         Bang(x, y + 1);
6925         return;
6926       }
6927     }
6928     else if (((element == EL_SP_INFOTRON ||
6929                element == EL_SP_ZONK) &&
6930               (smashed == EL_SP_SNIKSNAK ||
6931                smashed == EL_SP_ELECTRON ||
6932                smashed == EL_SP_DISK_ORANGE)) ||
6933              (element == EL_SP_INFOTRON &&
6934               smashed == EL_SP_DISK_YELLOW))
6935     {
6936       Bang(x, y + 1);
6937       return;
6938     }
6939     else if (CAN_SMASH_EVERYTHING(element))
6940     {
6941       if (IS_CLASSIC_ENEMY(smashed) ||
6942           CAN_EXPLODE_SMASHED(smashed))
6943       {
6944         Bang(x, y + 1);
6945         return;
6946       }
6947       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6948       {
6949         if (smashed == EL_LAMP ||
6950             smashed == EL_LAMP_ACTIVE)
6951         {
6952           Bang(x, y + 1);
6953           return;
6954         }
6955         else if (smashed == EL_NUT)
6956         {
6957           Feld[x][y + 1] = EL_NUT_BREAKING;
6958           PlayLevelSound(x, y, SND_NUT_BREAKING);
6959           RaiseScoreElement(EL_NUT);
6960           return;
6961         }
6962         else if (smashed == EL_PEARL)
6963         {
6964           ResetGfxAnimation(x, y);
6965
6966           Feld[x][y + 1] = EL_PEARL_BREAKING;
6967           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968           return;
6969         }
6970         else if (smashed == EL_DIAMOND)
6971         {
6972           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6973           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6974           return;
6975         }
6976         else if (IS_BELT_SWITCH(smashed))
6977         {
6978           ToggleBeltSwitch(x, y + 1);
6979         }
6980         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6981                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6982                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6983                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6984         {
6985           ToggleSwitchgateSwitch(x, y + 1);
6986         }
6987         else if (smashed == EL_LIGHT_SWITCH ||
6988                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6989         {
6990           ToggleLightSwitch(x, y + 1);
6991         }
6992         else
6993         {
6994 #if 0
6995           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6996 #endif
6997
6998           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6999
7000           CheckElementChangeBySide(x, y + 1, smashed, element,
7001                                    CE_SWITCHED, CH_SIDE_TOP);
7002           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7003                                             CH_SIDE_TOP);
7004         }
7005       }
7006       else
7007       {
7008         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7009       }
7010     }
7011   }
7012
7013   /* play sound of magic wall / mill */
7014   if (!last_line &&
7015       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7016        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7017        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7018   {
7019     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7020       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7021     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7022       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7023     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7024       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7025
7026     return;
7027   }
7028
7029   /* play sound of object that hits the ground */
7030   if (last_line || object_hit)
7031     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7032 }
7033
7034 inline static void TurnRoundExt(int x, int y)
7035 {
7036   static struct
7037   {
7038     int dx, dy;
7039   } move_xy[] =
7040   {
7041     {  0,  0 },
7042     { -1,  0 },
7043     { +1,  0 },
7044     {  0,  0 },
7045     {  0, -1 },
7046     {  0,  0 }, { 0, 0 }, { 0, 0 },
7047     {  0, +1 }
7048   };
7049   static struct
7050   {
7051     int left, right, back;
7052   } turn[] =
7053   {
7054     { 0,        0,              0        },
7055     { MV_DOWN,  MV_UP,          MV_RIGHT },
7056     { MV_UP,    MV_DOWN,        MV_LEFT  },
7057     { 0,        0,              0        },
7058     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7059     { 0,        0,              0        },
7060     { 0,        0,              0        },
7061     { 0,        0,              0        },
7062     { MV_RIGHT, MV_LEFT,        MV_UP    }
7063   };
7064
7065   int element = Feld[x][y];
7066   int move_pattern = element_info[element].move_pattern;
7067
7068   int old_move_dir = MovDir[x][y];
7069   int left_dir  = turn[old_move_dir].left;
7070   int right_dir = turn[old_move_dir].right;
7071   int back_dir  = turn[old_move_dir].back;
7072
7073   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7074   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7075   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7076   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7077
7078   int left_x  = x + left_dx,  left_y  = y + left_dy;
7079   int right_x = x + right_dx, right_y = y + right_dy;
7080   int move_x  = x + move_dx,  move_y  = y + move_dy;
7081
7082   int xx, yy;
7083
7084   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7085   {
7086     TestIfBadThingTouchesOtherBadThing(x, y);
7087
7088     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7089       MovDir[x][y] = right_dir;
7090     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7091       MovDir[x][y] = left_dir;
7092
7093     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7094       MovDelay[x][y] = 9;
7095     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7096       MovDelay[x][y] = 1;
7097   }
7098   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7099   {
7100     TestIfBadThingTouchesOtherBadThing(x, y);
7101
7102     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7103       MovDir[x][y] = left_dir;
7104     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7105       MovDir[x][y] = right_dir;
7106
7107     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7108       MovDelay[x][y] = 9;
7109     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7110       MovDelay[x][y] = 1;
7111   }
7112   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7113   {
7114     TestIfBadThingTouchesOtherBadThing(x, y);
7115
7116     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7117       MovDir[x][y] = left_dir;
7118     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7119       MovDir[x][y] = right_dir;
7120
7121     if (MovDir[x][y] != old_move_dir)
7122       MovDelay[x][y] = 9;
7123   }
7124   else if (element == EL_YAMYAM)
7125   {
7126     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7127     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7128
7129     if (can_turn_left && can_turn_right)
7130       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7131     else if (can_turn_left)
7132       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7133     else if (can_turn_right)
7134       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7135     else
7136       MovDir[x][y] = back_dir;
7137
7138     MovDelay[x][y] = 16 + 16 * RND(3);
7139   }
7140   else if (element == EL_DARK_YAMYAM)
7141   {
7142     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7143                                                          left_x, left_y);
7144     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7145                                                          right_x, right_y);
7146
7147     if (can_turn_left && can_turn_right)
7148       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149     else if (can_turn_left)
7150       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151     else if (can_turn_right)
7152       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7153     else
7154       MovDir[x][y] = back_dir;
7155
7156     MovDelay[x][y] = 16 + 16 * RND(3);
7157   }
7158   else if (element == EL_PACMAN)
7159   {
7160     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7161     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7162
7163     if (can_turn_left && can_turn_right)
7164       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7165     else if (can_turn_left)
7166       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7167     else if (can_turn_right)
7168       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7169     else
7170       MovDir[x][y] = back_dir;
7171
7172     MovDelay[x][y] = 6 + RND(40);
7173   }
7174   else if (element == EL_PIG)
7175   {
7176     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7177     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7178     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7179     boolean should_turn_left, should_turn_right, should_move_on;
7180     int rnd_value = 24;
7181     int rnd = RND(rnd_value);
7182
7183     should_turn_left = (can_turn_left &&
7184                         (!can_move_on ||
7185                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7186                                                    y + back_dy + left_dy)));
7187     should_turn_right = (can_turn_right &&
7188                          (!can_move_on ||
7189                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7190                                                     y + back_dy + right_dy)));
7191     should_move_on = (can_move_on &&
7192                       (!can_turn_left ||
7193                        !can_turn_right ||
7194                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7195                                                  y + move_dy + left_dy) ||
7196                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7197                                                  y + move_dy + right_dy)));
7198
7199     if (should_turn_left || should_turn_right || should_move_on)
7200     {
7201       if (should_turn_left && should_turn_right && should_move_on)
7202         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7203                         rnd < 2 * rnd_value / 3 ? right_dir :
7204                         old_move_dir);
7205       else if (should_turn_left && should_turn_right)
7206         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7207       else if (should_turn_left && should_move_on)
7208         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7209       else if (should_turn_right && should_move_on)
7210         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7211       else if (should_turn_left)
7212         MovDir[x][y] = left_dir;
7213       else if (should_turn_right)
7214         MovDir[x][y] = right_dir;
7215       else if (should_move_on)
7216         MovDir[x][y] = old_move_dir;
7217     }
7218     else if (can_move_on && rnd > rnd_value / 8)
7219       MovDir[x][y] = old_move_dir;
7220     else if (can_turn_left && can_turn_right)
7221       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7222     else if (can_turn_left && rnd > rnd_value / 8)
7223       MovDir[x][y] = left_dir;
7224     else if (can_turn_right && rnd > rnd_value/8)
7225       MovDir[x][y] = right_dir;
7226     else
7227       MovDir[x][y] = back_dir;
7228
7229     xx = x + move_xy[MovDir[x][y]].dx;
7230     yy = y + move_xy[MovDir[x][y]].dy;
7231
7232     if (!IN_LEV_FIELD(xx, yy) ||
7233         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7234       MovDir[x][y] = old_move_dir;
7235
7236     MovDelay[x][y] = 0;
7237   }
7238   else if (element == EL_DRAGON)
7239   {
7240     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7241     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7242     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7243     int rnd_value = 24;
7244     int rnd = RND(rnd_value);
7245
7246     if (can_move_on && rnd > rnd_value / 8)
7247       MovDir[x][y] = old_move_dir;
7248     else if (can_turn_left && can_turn_right)
7249       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7250     else if (can_turn_left && rnd > rnd_value / 8)
7251       MovDir[x][y] = left_dir;
7252     else if (can_turn_right && rnd > rnd_value / 8)
7253       MovDir[x][y] = right_dir;
7254     else
7255       MovDir[x][y] = back_dir;
7256
7257     xx = x + move_xy[MovDir[x][y]].dx;
7258     yy = y + move_xy[MovDir[x][y]].dy;
7259
7260     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7261       MovDir[x][y] = old_move_dir;
7262
7263     MovDelay[x][y] = 0;
7264   }
7265   else if (element == EL_MOLE)
7266   {
7267     boolean can_move_on =
7268       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7269                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7270                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7271     if (!can_move_on)
7272     {
7273       boolean can_turn_left =
7274         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7275                               IS_AMOEBOID(Feld[left_x][left_y])));
7276
7277       boolean can_turn_right =
7278         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7279                               IS_AMOEBOID(Feld[right_x][right_y])));
7280
7281       if (can_turn_left && can_turn_right)
7282         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7283       else if (can_turn_left)
7284         MovDir[x][y] = left_dir;
7285       else
7286         MovDir[x][y] = right_dir;
7287     }
7288
7289     if (MovDir[x][y] != old_move_dir)
7290       MovDelay[x][y] = 9;
7291   }
7292   else if (element == EL_BALLOON)
7293   {
7294     MovDir[x][y] = game.wind_direction;
7295     MovDelay[x][y] = 0;
7296   }
7297   else if (element == EL_SPRING)
7298   {
7299 #if USE_NEW_SPRING_BUMPER
7300     if (MovDir[x][y] & MV_HORIZONTAL)
7301     {
7302       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7303           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304       {
7305         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7306         ResetGfxAnimation(move_x, move_y);
7307         TEST_DrawLevelField(move_x, move_y);
7308
7309         MovDir[x][y] = back_dir;
7310       }
7311       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7312                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7313         MovDir[x][y] = MV_NONE;
7314     }
7315 #else
7316     if (MovDir[x][y] & MV_HORIZONTAL &&
7317         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7318          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7319       MovDir[x][y] = MV_NONE;
7320 #endif
7321
7322     MovDelay[x][y] = 0;
7323   }
7324   else if (element == EL_ROBOT ||
7325            element == EL_SATELLITE ||
7326            element == EL_PENGUIN ||
7327            element == EL_EMC_ANDROID)
7328   {
7329     int attr_x = -1, attr_y = -1;
7330
7331     if (AllPlayersGone)
7332     {
7333       attr_x = ExitX;
7334       attr_y = ExitY;
7335     }
7336     else
7337     {
7338       int i;
7339
7340       for (i = 0; i < MAX_PLAYERS; i++)
7341       {
7342         struct PlayerInfo *player = &stored_player[i];
7343         int jx = player->jx, jy = player->jy;
7344
7345         if (!player->active)
7346           continue;
7347
7348         if (attr_x == -1 ||
7349             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7350         {
7351           attr_x = jx;
7352           attr_y = jy;
7353         }
7354       }
7355     }
7356
7357     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7358         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7359          game.engine_version < VERSION_IDENT(3,1,0,0)))
7360     {
7361       attr_x = ZX;
7362       attr_y = ZY;
7363     }
7364
7365     if (element == EL_PENGUIN)
7366     {
7367       int i;
7368       static int xy[4][2] =
7369       {
7370         { 0, -1 },
7371         { -1, 0 },
7372         { +1, 0 },
7373         { 0, +1 }
7374       };
7375
7376       for (i = 0; i < NUM_DIRECTIONS; i++)
7377       {
7378         int ex = x + xy[i][0];
7379         int ey = y + xy[i][1];
7380
7381         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7382                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7383                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7384                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7385         {
7386           attr_x = ex;
7387           attr_y = ey;
7388           break;
7389         }
7390       }
7391     }
7392
7393     MovDir[x][y] = MV_NONE;
7394     if (attr_x < x)
7395       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7396     else if (attr_x > x)
7397       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7398     if (attr_y < y)
7399       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7400     else if (attr_y > y)
7401       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7402
7403     if (element == EL_ROBOT)
7404     {
7405       int newx, newy;
7406
7407       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7408         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7409       Moving2Blocked(x, y, &newx, &newy);
7410
7411       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7412         MovDelay[x][y] = 8 + 8 * !RND(3);
7413       else
7414         MovDelay[x][y] = 16;
7415     }
7416     else if (element == EL_PENGUIN)
7417     {
7418       int newx, newy;
7419
7420       MovDelay[x][y] = 1;
7421
7422       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7423       {
7424         boolean first_horiz = RND(2);
7425         int new_move_dir = MovDir[x][y];
7426
7427         MovDir[x][y] =
7428           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7429         Moving2Blocked(x, y, &newx, &newy);
7430
7431         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7432           return;
7433
7434         MovDir[x][y] =
7435           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7436         Moving2Blocked(x, y, &newx, &newy);
7437
7438         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7439           return;
7440
7441         MovDir[x][y] = old_move_dir;
7442         return;
7443       }
7444     }
7445     else if (element == EL_SATELLITE)
7446     {
7447       int newx, newy;
7448
7449       MovDelay[x][y] = 1;
7450
7451       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7452       {
7453         boolean first_horiz = RND(2);
7454         int new_move_dir = MovDir[x][y];
7455
7456         MovDir[x][y] =
7457           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7458         Moving2Blocked(x, y, &newx, &newy);
7459
7460         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7461           return;
7462
7463         MovDir[x][y] =
7464           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7465         Moving2Blocked(x, y, &newx, &newy);
7466
7467         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7468           return;
7469
7470         MovDir[x][y] = old_move_dir;
7471         return;
7472       }
7473     }
7474     else if (element == EL_EMC_ANDROID)
7475     {
7476       static int check_pos[16] =
7477       {
7478         -1,             /*  0 => (invalid)          */
7479         7,              /*  1 => MV_LEFT            */
7480         3,              /*  2 => MV_RIGHT           */
7481         -1,             /*  3 => (invalid)          */
7482         1,              /*  4 =>            MV_UP   */
7483         0,              /*  5 => MV_LEFT  | MV_UP   */
7484         2,              /*  6 => MV_RIGHT | MV_UP   */
7485         -1,             /*  7 => (invalid)          */
7486         5,              /*  8 =>            MV_DOWN */
7487         6,              /*  9 => MV_LEFT  | MV_DOWN */
7488         4,              /* 10 => MV_RIGHT | MV_DOWN */
7489         -1,             /* 11 => (invalid)          */
7490         -1,             /* 12 => (invalid)          */
7491         -1,             /* 13 => (invalid)          */
7492         -1,             /* 14 => (invalid)          */
7493         -1,             /* 15 => (invalid)          */
7494       };
7495       static struct
7496       {
7497         int dx, dy;
7498         int dir;
7499       } check_xy[8] =
7500       {
7501         { -1, -1,       MV_LEFT  | MV_UP   },
7502         {  0, -1,                  MV_UP   },
7503         { +1, -1,       MV_RIGHT | MV_UP   },
7504         { +1,  0,       MV_RIGHT           },
7505         { +1, +1,       MV_RIGHT | MV_DOWN },
7506         {  0, +1,                  MV_DOWN },
7507         { -1, +1,       MV_LEFT  | MV_DOWN },
7508         { -1,  0,       MV_LEFT            },
7509       };
7510       int start_pos, check_order;
7511       boolean can_clone = FALSE;
7512       int i;
7513
7514       /* check if there is any free field around current position */
7515       for (i = 0; i < 8; i++)
7516       {
7517         int newx = x + check_xy[i].dx;
7518         int newy = y + check_xy[i].dy;
7519
7520         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7521         {
7522           can_clone = TRUE;
7523
7524           break;
7525         }
7526       }
7527
7528       if (can_clone)            /* randomly find an element to clone */
7529       {
7530         can_clone = FALSE;
7531
7532         start_pos = check_pos[RND(8)];
7533         check_order = (RND(2) ? -1 : +1);
7534
7535         for (i = 0; i < 8; i++)
7536         {
7537           int pos_raw = start_pos + i * check_order;
7538           int pos = (pos_raw + 8) % 8;
7539           int newx = x + check_xy[pos].dx;
7540           int newy = y + check_xy[pos].dy;
7541
7542           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7543           {
7544             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7545             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7546
7547             Store[x][y] = Feld[newx][newy];
7548
7549             can_clone = TRUE;
7550
7551             break;
7552           }
7553         }
7554       }
7555
7556       if (can_clone)            /* randomly find a direction to move */
7557       {
7558         can_clone = FALSE;
7559
7560         start_pos = check_pos[RND(8)];
7561         check_order = (RND(2) ? -1 : +1);
7562
7563         for (i = 0; i < 8; i++)
7564         {
7565           int pos_raw = start_pos + i * check_order;
7566           int pos = (pos_raw + 8) % 8;
7567           int newx = x + check_xy[pos].dx;
7568           int newy = y + check_xy[pos].dy;
7569           int new_move_dir = check_xy[pos].dir;
7570
7571           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7572           {
7573             MovDir[x][y] = new_move_dir;
7574             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7575
7576             can_clone = TRUE;
7577
7578             break;
7579           }
7580         }
7581       }
7582
7583       if (can_clone)            /* cloning and moving successful */
7584         return;
7585
7586       /* cannot clone -- try to move towards player */
7587
7588       start_pos = check_pos[MovDir[x][y] & 0x0f];
7589       check_order = (RND(2) ? -1 : +1);
7590
7591       for (i = 0; i < 3; i++)
7592       {
7593         /* first check start_pos, then previous/next or (next/previous) pos */
7594         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7595         int pos = (pos_raw + 8) % 8;
7596         int newx = x + check_xy[pos].dx;
7597         int newy = y + check_xy[pos].dy;
7598         int new_move_dir = check_xy[pos].dir;
7599
7600         if (IS_PLAYER(newx, newy))
7601           break;
7602
7603         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7604         {
7605           MovDir[x][y] = new_move_dir;
7606           MovDelay[x][y] = level.android_move_time * 8 + 1;
7607
7608           break;
7609         }
7610       }
7611     }
7612   }
7613   else if (move_pattern == MV_TURNING_LEFT ||
7614            move_pattern == MV_TURNING_RIGHT ||
7615            move_pattern == MV_TURNING_LEFT_RIGHT ||
7616            move_pattern == MV_TURNING_RIGHT_LEFT ||
7617            move_pattern == MV_TURNING_RANDOM ||
7618            move_pattern == MV_ALL_DIRECTIONS)
7619   {
7620     boolean can_turn_left =
7621       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7622     boolean can_turn_right =
7623       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7624
7625     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7626       return;
7627
7628     if (move_pattern == MV_TURNING_LEFT)
7629       MovDir[x][y] = left_dir;
7630     else if (move_pattern == MV_TURNING_RIGHT)
7631       MovDir[x][y] = right_dir;
7632     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7633       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7634     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7635       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7636     else if (move_pattern == MV_TURNING_RANDOM)
7637       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7638                       can_turn_right && !can_turn_left ? right_dir :
7639                       RND(2) ? left_dir : right_dir);
7640     else if (can_turn_left && can_turn_right)
7641       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7642     else if (can_turn_left)
7643       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7644     else if (can_turn_right)
7645       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7646     else
7647       MovDir[x][y] = back_dir;
7648
7649     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7650   }
7651   else if (move_pattern == MV_HORIZONTAL ||
7652            move_pattern == MV_VERTICAL)
7653   {
7654     if (move_pattern & old_move_dir)
7655       MovDir[x][y] = back_dir;
7656     else if (move_pattern == MV_HORIZONTAL)
7657       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7658     else if (move_pattern == MV_VERTICAL)
7659       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7660
7661     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7662   }
7663   else if (move_pattern & MV_ANY_DIRECTION)
7664   {
7665     MovDir[x][y] = move_pattern;
7666     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7667   }
7668   else if (move_pattern & MV_WIND_DIRECTION)
7669   {
7670     MovDir[x][y] = game.wind_direction;
7671     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7672   }
7673   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7674   {
7675     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7676       MovDir[x][y] = left_dir;
7677     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7678       MovDir[x][y] = right_dir;
7679
7680     if (MovDir[x][y] != old_move_dir)
7681       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7682   }
7683   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7684   {
7685     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7686       MovDir[x][y] = right_dir;
7687     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7688       MovDir[x][y] = left_dir;
7689
7690     if (MovDir[x][y] != old_move_dir)
7691       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7692   }
7693   else if (move_pattern == MV_TOWARDS_PLAYER ||
7694            move_pattern == MV_AWAY_FROM_PLAYER)
7695   {
7696     int attr_x = -1, attr_y = -1;
7697     int newx, newy;
7698     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7699
7700     if (AllPlayersGone)
7701     {
7702       attr_x = ExitX;
7703       attr_y = ExitY;
7704     }
7705     else
7706     {
7707       int i;
7708
7709       for (i = 0; i < MAX_PLAYERS; i++)
7710       {
7711         struct PlayerInfo *player = &stored_player[i];
7712         int jx = player->jx, jy = player->jy;
7713
7714         if (!player->active)
7715           continue;
7716
7717         if (attr_x == -1 ||
7718             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7719         {
7720           attr_x = jx;
7721           attr_y = jy;
7722         }
7723       }
7724     }
7725
7726     MovDir[x][y] = MV_NONE;
7727     if (attr_x < x)
7728       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7729     else if (attr_x > x)
7730       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7731     if (attr_y < y)
7732       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7733     else if (attr_y > y)
7734       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7735
7736     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7737
7738     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7739     {
7740       boolean first_horiz = RND(2);
7741       int new_move_dir = MovDir[x][y];
7742
7743       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7744       {
7745         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7746         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7747
7748         return;
7749       }
7750
7751       MovDir[x][y] =
7752         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7753       Moving2Blocked(x, y, &newx, &newy);
7754
7755       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7756         return;
7757
7758       MovDir[x][y] =
7759         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7760       Moving2Blocked(x, y, &newx, &newy);
7761
7762       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7763         return;
7764
7765       MovDir[x][y] = old_move_dir;
7766     }
7767   }
7768   else if (move_pattern == MV_WHEN_PUSHED ||
7769            move_pattern == MV_WHEN_DROPPED)
7770   {
7771     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7772       MovDir[x][y] = MV_NONE;
7773
7774     MovDelay[x][y] = 0;
7775   }
7776   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7777   {
7778     static int test_xy[7][2] =
7779     {
7780       { 0, -1 },
7781       { -1, 0 },
7782       { +1, 0 },
7783       { 0, +1 },
7784       { 0, -1 },
7785       { -1, 0 },
7786       { +1, 0 },
7787     };
7788     static int test_dir[7] =
7789     {
7790       MV_UP,
7791       MV_LEFT,
7792       MV_RIGHT,
7793       MV_DOWN,
7794       MV_UP,
7795       MV_LEFT,
7796       MV_RIGHT,
7797     };
7798     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7799     int move_preference = -1000000;     /* start with very low preference */
7800     int new_move_dir = MV_NONE;
7801     int start_test = RND(4);
7802     int i;
7803
7804     for (i = 0; i < NUM_DIRECTIONS; i++)
7805     {
7806       int move_dir = test_dir[start_test + i];
7807       int move_dir_preference;
7808
7809       xx = x + test_xy[start_test + i][0];
7810       yy = y + test_xy[start_test + i][1];
7811
7812       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7813           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7814       {
7815         new_move_dir = move_dir;
7816
7817         break;
7818       }
7819
7820       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7821         continue;
7822
7823       move_dir_preference = -1 * RunnerVisit[xx][yy];
7824       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7825         move_dir_preference = PlayerVisit[xx][yy];
7826
7827       if (move_dir_preference > move_preference)
7828       {
7829         /* prefer field that has not been visited for the longest time */
7830         move_preference = move_dir_preference;
7831         new_move_dir = move_dir;
7832       }
7833       else if (move_dir_preference == move_preference &&
7834                move_dir == old_move_dir)
7835       {
7836         /* prefer last direction when all directions are preferred equally */
7837         move_preference = move_dir_preference;
7838         new_move_dir = move_dir;
7839       }
7840     }
7841
7842     MovDir[x][y] = new_move_dir;
7843     if (old_move_dir != new_move_dir)
7844       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7845   }
7846 }
7847
7848 static void TurnRound(int x, int y)
7849 {
7850   int direction = MovDir[x][y];
7851
7852   TurnRoundExt(x, y);
7853
7854   GfxDir[x][y] = MovDir[x][y];
7855
7856   if (direction != MovDir[x][y])
7857     GfxFrame[x][y] = 0;
7858
7859   if (MovDelay[x][y])
7860     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7861
7862   ResetGfxFrame(x, y, FALSE);
7863 }
7864
7865 static boolean JustBeingPushed(int x, int y)
7866 {
7867   int i;
7868
7869   for (i = 0; i < MAX_PLAYERS; i++)
7870   {
7871     struct PlayerInfo *player = &stored_player[i];
7872
7873     if (player->active && player->is_pushing && player->MovPos)
7874     {
7875       int next_jx = player->jx + (player->jx - player->last_jx);
7876       int next_jy = player->jy + (player->jy - player->last_jy);
7877
7878       if (x == next_jx && y == next_jy)
7879         return TRUE;
7880     }
7881   }
7882
7883   return FALSE;
7884 }
7885
7886 void StartMoving(int x, int y)
7887 {
7888   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7889   int element = Feld[x][y];
7890
7891   if (Stop[x][y])
7892     return;
7893
7894   if (MovDelay[x][y] == 0)
7895     GfxAction[x][y] = ACTION_DEFAULT;
7896
7897   if (CAN_FALL(element) && y < lev_fieldy - 1)
7898   {
7899     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7900         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7901       if (JustBeingPushed(x, y))
7902         return;
7903
7904     if (element == EL_QUICKSAND_FULL)
7905     {
7906       if (IS_FREE(x, y + 1))
7907       {
7908         InitMovingField(x, y, MV_DOWN);
7909         started_moving = TRUE;
7910
7911         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7912 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7913         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7914           Store[x][y] = EL_ROCK;
7915 #else
7916         Store[x][y] = EL_ROCK;
7917 #endif
7918
7919         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7920       }
7921       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7922       {
7923         if (!MovDelay[x][y])
7924         {
7925           MovDelay[x][y] = TILEY + 1;
7926
7927           ResetGfxAnimation(x, y);
7928           ResetGfxAnimation(x, y + 1);
7929         }
7930
7931         if (MovDelay[x][y])
7932         {
7933           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7934           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7935
7936           MovDelay[x][y]--;
7937           if (MovDelay[x][y])
7938             return;
7939         }
7940
7941         Feld[x][y] = EL_QUICKSAND_EMPTY;
7942         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7943         Store[x][y + 1] = Store[x][y];
7944         Store[x][y] = 0;
7945
7946         PlayLevelSoundAction(x, y, ACTION_FILLING);
7947       }
7948       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7949       {
7950         if (!MovDelay[x][y])
7951         {
7952           MovDelay[x][y] = TILEY + 1;
7953
7954           ResetGfxAnimation(x, y);
7955           ResetGfxAnimation(x, y + 1);
7956         }
7957
7958         if (MovDelay[x][y])
7959         {
7960           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7961           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7962
7963           MovDelay[x][y]--;
7964           if (MovDelay[x][y])
7965             return;
7966         }
7967
7968         Feld[x][y] = EL_QUICKSAND_EMPTY;
7969         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7970         Store[x][y + 1] = Store[x][y];
7971         Store[x][y] = 0;
7972
7973         PlayLevelSoundAction(x, y, ACTION_FILLING);
7974       }
7975     }
7976     else if (element == EL_QUICKSAND_FAST_FULL)
7977     {
7978       if (IS_FREE(x, y + 1))
7979       {
7980         InitMovingField(x, y, MV_DOWN);
7981         started_moving = TRUE;
7982
7983         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7984 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7985         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7986           Store[x][y] = EL_ROCK;
7987 #else
7988         Store[x][y] = EL_ROCK;
7989 #endif
7990
7991         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7992       }
7993       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7994       {
7995         if (!MovDelay[x][y])
7996         {
7997           MovDelay[x][y] = TILEY + 1;
7998
7999           ResetGfxAnimation(x, y);
8000           ResetGfxAnimation(x, y + 1);
8001         }
8002
8003         if (MovDelay[x][y])
8004         {
8005           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8006           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8007
8008           MovDelay[x][y]--;
8009           if (MovDelay[x][y])
8010             return;
8011         }
8012
8013         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8014         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8015         Store[x][y + 1] = Store[x][y];
8016         Store[x][y] = 0;
8017
8018         PlayLevelSoundAction(x, y, ACTION_FILLING);
8019       }
8020       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8021       {
8022         if (!MovDelay[x][y])
8023         {
8024           MovDelay[x][y] = TILEY + 1;
8025
8026           ResetGfxAnimation(x, y);
8027           ResetGfxAnimation(x, y + 1);
8028         }
8029
8030         if (MovDelay[x][y])
8031         {
8032           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8033           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8034
8035           MovDelay[x][y]--;
8036           if (MovDelay[x][y])
8037             return;
8038         }
8039
8040         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8041         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8042         Store[x][y + 1] = Store[x][y];
8043         Store[x][y] = 0;
8044
8045         PlayLevelSoundAction(x, y, ACTION_FILLING);
8046       }
8047     }
8048     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8049              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8050     {
8051       InitMovingField(x, y, MV_DOWN);
8052       started_moving = TRUE;
8053
8054       Feld[x][y] = EL_QUICKSAND_FILLING;
8055       Store[x][y] = element;
8056
8057       PlayLevelSoundAction(x, y, ACTION_FILLING);
8058     }
8059     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8060              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8061     {
8062       InitMovingField(x, y, MV_DOWN);
8063       started_moving = TRUE;
8064
8065       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8066       Store[x][y] = element;
8067
8068       PlayLevelSoundAction(x, y, ACTION_FILLING);
8069     }
8070     else if (element == EL_MAGIC_WALL_FULL)
8071     {
8072       if (IS_FREE(x, y + 1))
8073       {
8074         InitMovingField(x, y, MV_DOWN);
8075         started_moving = TRUE;
8076
8077         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8078         Store[x][y] = EL_CHANGED(Store[x][y]);
8079       }
8080       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8081       {
8082         if (!MovDelay[x][y])
8083           MovDelay[x][y] = TILEY/4 + 1;
8084
8085         if (MovDelay[x][y])
8086         {
8087           MovDelay[x][y]--;
8088           if (MovDelay[x][y])
8089             return;
8090         }
8091
8092         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8093         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8094         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8095         Store[x][y] = 0;
8096       }
8097     }
8098     else if (element == EL_BD_MAGIC_WALL_FULL)
8099     {
8100       if (IS_FREE(x, y + 1))
8101       {
8102         InitMovingField(x, y, MV_DOWN);
8103         started_moving = TRUE;
8104
8105         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8106         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8107       }
8108       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8109       {
8110         if (!MovDelay[x][y])
8111           MovDelay[x][y] = TILEY/4 + 1;
8112
8113         if (MovDelay[x][y])
8114         {
8115           MovDelay[x][y]--;
8116           if (MovDelay[x][y])
8117             return;
8118         }
8119
8120         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8121         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8122         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8123         Store[x][y] = 0;
8124       }
8125     }
8126     else if (element == EL_DC_MAGIC_WALL_FULL)
8127     {
8128       if (IS_FREE(x, y + 1))
8129       {
8130         InitMovingField(x, y, MV_DOWN);
8131         started_moving = TRUE;
8132
8133         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8134         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8135       }
8136       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8137       {
8138         if (!MovDelay[x][y])
8139           MovDelay[x][y] = TILEY/4 + 1;
8140
8141         if (MovDelay[x][y])
8142         {
8143           MovDelay[x][y]--;
8144           if (MovDelay[x][y])
8145             return;
8146         }
8147
8148         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8149         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8150         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8151         Store[x][y] = 0;
8152       }
8153     }
8154     else if ((CAN_PASS_MAGIC_WALL(element) &&
8155               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8156                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8157              (CAN_PASS_DC_MAGIC_WALL(element) &&
8158               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8159
8160     {
8161       InitMovingField(x, y, MV_DOWN);
8162       started_moving = TRUE;
8163
8164       Feld[x][y] =
8165         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8166          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8167          EL_DC_MAGIC_WALL_FILLING);
8168       Store[x][y] = element;
8169     }
8170     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8171     {
8172       SplashAcid(x, y + 1);
8173
8174       InitMovingField(x, y, MV_DOWN);
8175       started_moving = TRUE;
8176
8177       Store[x][y] = EL_ACID;
8178     }
8179     else if (
8180 #if USE_FIX_IMPACT_COLLISION
8181              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8182               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8183 #else
8184              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8185               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8186 #endif
8187              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8188               CAN_FALL(element) && WasJustFalling[x][y] &&
8189               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8190
8191              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8192               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8193               (Feld[x][y + 1] == EL_BLOCKED)))
8194     {
8195       /* this is needed for a special case not covered by calling "Impact()"
8196          from "ContinueMoving()": if an element moves to a tile directly below
8197          another element which was just falling on that tile (which was empty
8198          in the previous frame), the falling element above would just stop
8199          instead of smashing the element below (in previous version, the above
8200          element was just checked for "moving" instead of "falling", resulting
8201          in incorrect smashes caused by horizontal movement of the above
8202          element; also, the case of the player being the element to smash was
8203          simply not covered here... :-/ ) */
8204
8205       CheckCollision[x][y] = 0;
8206       CheckImpact[x][y] = 0;
8207
8208       Impact(x, y);
8209     }
8210     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8211     {
8212       if (MovDir[x][y] == MV_NONE)
8213       {
8214         InitMovingField(x, y, MV_DOWN);
8215         started_moving = TRUE;
8216       }
8217     }
8218     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8219     {
8220       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8221         MovDir[x][y] = MV_DOWN;
8222
8223       InitMovingField(x, y, MV_DOWN);
8224       started_moving = TRUE;
8225     }
8226     else if (element == EL_AMOEBA_DROP)
8227     {
8228       Feld[x][y] = EL_AMOEBA_GROWING;
8229       Store[x][y] = EL_AMOEBA_WET;
8230     }
8231     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8232               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8233              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8234              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8235     {
8236       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8237                                 (IS_FREE(x - 1, y + 1) ||
8238                                  Feld[x - 1][y + 1] == EL_ACID));
8239       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8240                                 (IS_FREE(x + 1, y + 1) ||
8241                                  Feld[x + 1][y + 1] == EL_ACID));
8242       boolean can_fall_any  = (can_fall_left || can_fall_right);
8243       boolean can_fall_both = (can_fall_left && can_fall_right);
8244       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8245
8246 #if USE_NEW_ALL_SLIPPERY
8247       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8248       {
8249         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8250           can_fall_right = FALSE;
8251         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8252           can_fall_left = FALSE;
8253         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8254           can_fall_right = FALSE;
8255         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8256           can_fall_left = FALSE;
8257
8258         can_fall_any  = (can_fall_left || can_fall_right);
8259         can_fall_both = FALSE;
8260       }
8261 #else
8262       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8263       {
8264         if (slippery_type == SLIPPERY_ONLY_LEFT)
8265           can_fall_right = FALSE;
8266         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8267           can_fall_left = FALSE;
8268         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8269           can_fall_right = FALSE;
8270         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8271           can_fall_left = FALSE;
8272
8273         can_fall_any  = (can_fall_left || can_fall_right);
8274         can_fall_both = (can_fall_left && can_fall_right);
8275       }
8276 #endif
8277
8278 #if USE_NEW_ALL_SLIPPERY
8279 #else
8280 #if USE_NEW_SP_SLIPPERY
8281       /* !!! better use the same properties as for custom elements here !!! */
8282       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8283                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8284       {
8285         can_fall_right = FALSE;         /* slip down on left side */
8286         can_fall_both = FALSE;
8287       }
8288 #endif
8289 #endif
8290
8291 #if USE_NEW_ALL_SLIPPERY
8292       if (can_fall_both)
8293       {
8294         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8295           can_fall_right = FALSE;       /* slip down on left side */
8296         else
8297           can_fall_left = !(can_fall_right = RND(2));
8298
8299         can_fall_both = FALSE;
8300       }
8301 #else
8302       if (can_fall_both)
8303       {
8304         if (game.emulation == EMU_BOULDERDASH ||
8305             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8306           can_fall_right = FALSE;       /* slip down on left side */
8307         else
8308           can_fall_left = !(can_fall_right = RND(2));
8309
8310         can_fall_both = FALSE;
8311       }
8312 #endif
8313
8314       if (can_fall_any)
8315       {
8316         /* if not determined otherwise, prefer left side for slipping down */
8317         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8318         started_moving = TRUE;
8319       }
8320     }
8321 #if 0
8322     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8323 #else
8324     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8325 #endif
8326     {
8327       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8328       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8329       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8330       int belt_dir = game.belt_dir[belt_nr];
8331
8332       if ((belt_dir == MV_LEFT  && left_is_free) ||
8333           (belt_dir == MV_RIGHT && right_is_free))
8334       {
8335         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8336
8337         InitMovingField(x, y, belt_dir);
8338         started_moving = TRUE;
8339
8340         Pushed[x][y] = TRUE;
8341         Pushed[nextx][y] = TRUE;
8342
8343         GfxAction[x][y] = ACTION_DEFAULT;
8344       }
8345       else
8346       {
8347         MovDir[x][y] = 0;       /* if element was moving, stop it */
8348       }
8349     }
8350   }
8351
8352   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8353 #if 0
8354   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8355 #else
8356   if (CAN_MOVE(element) && !started_moving)
8357 #endif
8358   {
8359     int move_pattern = element_info[element].move_pattern;
8360     int newx, newy;
8361
8362 #if 0
8363 #if DEBUG
8364     if (MovDir[x][y] == MV_NONE)
8365     {
8366       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8367              x, y, element, element_info[element].token_name);
8368       printf("StartMoving(): This should never happen!\n");
8369     }
8370 #endif
8371 #endif
8372
8373     Moving2Blocked(x, y, &newx, &newy);
8374
8375     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8376       return;
8377
8378     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8379         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8380     {
8381       WasJustMoving[x][y] = 0;
8382       CheckCollision[x][y] = 0;
8383
8384       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8385
8386       if (Feld[x][y] != element)        /* element has changed */
8387         return;
8388     }
8389
8390     if (!MovDelay[x][y])        /* start new movement phase */
8391     {
8392       /* all objects that can change their move direction after each step
8393          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8394
8395       if (element != EL_YAMYAM &&
8396           element != EL_DARK_YAMYAM &&
8397           element != EL_PACMAN &&
8398           !(move_pattern & MV_ANY_DIRECTION) &&
8399           move_pattern != MV_TURNING_LEFT &&
8400           move_pattern != MV_TURNING_RIGHT &&
8401           move_pattern != MV_TURNING_LEFT_RIGHT &&
8402           move_pattern != MV_TURNING_RIGHT_LEFT &&
8403           move_pattern != MV_TURNING_RANDOM)
8404       {
8405         TurnRound(x, y);
8406
8407         if (MovDelay[x][y] && (element == EL_BUG ||
8408                                element == EL_SPACESHIP ||
8409                                element == EL_SP_SNIKSNAK ||
8410                                element == EL_SP_ELECTRON ||
8411                                element == EL_MOLE))
8412           TEST_DrawLevelField(x, y);
8413       }
8414     }
8415
8416     if (MovDelay[x][y])         /* wait some time before next movement */
8417     {
8418       MovDelay[x][y]--;
8419
8420       if (element == EL_ROBOT ||
8421           element == EL_YAMYAM ||
8422           element == EL_DARK_YAMYAM)
8423       {
8424         DrawLevelElementAnimationIfNeeded(x, y, element);
8425         PlayLevelSoundAction(x, y, ACTION_WAITING);
8426       }
8427       else if (element == EL_SP_ELECTRON)
8428         DrawLevelElementAnimationIfNeeded(x, y, element);
8429       else if (element == EL_DRAGON)
8430       {
8431         int i;
8432         int dir = MovDir[x][y];
8433         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8434         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8435         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8436                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8437                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8438                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8439         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8440
8441         GfxAction[x][y] = ACTION_ATTACKING;
8442
8443         if (IS_PLAYER(x, y))
8444           DrawPlayerField(x, y);
8445         else
8446           TEST_DrawLevelField(x, y);
8447
8448         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8449
8450         for (i = 1; i <= 3; i++)
8451         {
8452           int xx = x + i * dx;
8453           int yy = y + i * dy;
8454           int sx = SCREENX(xx);
8455           int sy = SCREENY(yy);
8456           int flame_graphic = graphic + (i - 1);
8457
8458           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8459             break;
8460
8461           if (MovDelay[x][y])
8462           {
8463             int flamed = MovingOrBlocked2Element(xx, yy);
8464
8465             /* !!! */
8466 #if 0
8467             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8468               Bang(xx, yy);
8469             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8470               RemoveMovingField(xx, yy);
8471             else
8472               RemoveField(xx, yy);
8473 #else
8474             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8475               Bang(xx, yy);
8476             else
8477               RemoveMovingField(xx, yy);
8478 #endif
8479
8480             ChangeDelay[xx][yy] = 0;
8481
8482             Feld[xx][yy] = EL_FLAMES;
8483
8484             if (IN_SCR_FIELD(sx, sy))
8485             {
8486               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8487               DrawGraphic(sx, sy, flame_graphic, frame);
8488             }
8489           }
8490           else
8491           {
8492             if (Feld[xx][yy] == EL_FLAMES)
8493               Feld[xx][yy] = EL_EMPTY;
8494             TEST_DrawLevelField(xx, yy);
8495           }
8496         }
8497       }
8498
8499       if (MovDelay[x][y])       /* element still has to wait some time */
8500       {
8501         PlayLevelSoundAction(x, y, ACTION_WAITING);
8502
8503         return;
8504       }
8505     }
8506
8507     /* now make next step */
8508
8509     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8510
8511     if (DONT_COLLIDE_WITH(element) &&
8512         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8513         !PLAYER_ENEMY_PROTECTED(newx, newy))
8514     {
8515       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8516
8517       return;
8518     }
8519
8520     else if (CAN_MOVE_INTO_ACID(element) &&
8521              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8522              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8523              (MovDir[x][y] == MV_DOWN ||
8524               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8525     {
8526       SplashAcid(newx, newy);
8527       Store[x][y] = EL_ACID;
8528     }
8529     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8530     {
8531       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8532           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8533           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8534           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8535       {
8536         RemoveField(x, y);
8537         TEST_DrawLevelField(x, y);
8538
8539         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8540         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8541           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8542
8543         local_player->friends_still_needed--;
8544         if (!local_player->friends_still_needed &&
8545             !local_player->GameOver && AllPlayersGone)
8546           PlayerWins(local_player);
8547
8548         return;
8549       }
8550       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8551       {
8552         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8553           TEST_DrawLevelField(newx, newy);
8554         else
8555           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8556       }
8557       else if (!IS_FREE(newx, newy))
8558       {
8559         GfxAction[x][y] = ACTION_WAITING;
8560
8561         if (IS_PLAYER(x, y))
8562           DrawPlayerField(x, y);
8563         else
8564           TEST_DrawLevelField(x, y);
8565
8566         return;
8567       }
8568     }
8569     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8570     {
8571       if (IS_FOOD_PIG(Feld[newx][newy]))
8572       {
8573         if (IS_MOVING(newx, newy))
8574           RemoveMovingField(newx, newy);
8575         else
8576         {
8577           Feld[newx][newy] = EL_EMPTY;
8578           TEST_DrawLevelField(newx, newy);
8579         }
8580
8581         PlayLevelSound(x, y, SND_PIG_DIGGING);
8582       }
8583       else if (!IS_FREE(newx, newy))
8584       {
8585         if (IS_PLAYER(x, y))
8586           DrawPlayerField(x, y);
8587         else
8588           TEST_DrawLevelField(x, y);
8589
8590         return;
8591       }
8592     }
8593     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8594     {
8595       if (Store[x][y] != EL_EMPTY)
8596       {
8597         boolean can_clone = FALSE;
8598         int xx, yy;
8599
8600         /* check if element to clone is still there */
8601         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8602         {
8603           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8604           {
8605             can_clone = TRUE;
8606
8607             break;
8608           }
8609         }
8610
8611         /* cannot clone or target field not free anymore -- do not clone */
8612         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8613           Store[x][y] = EL_EMPTY;
8614       }
8615
8616       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8617       {
8618         if (IS_MV_DIAGONAL(MovDir[x][y]))
8619         {
8620           int diagonal_move_dir = MovDir[x][y];
8621           int stored = Store[x][y];
8622           int change_delay = 8;
8623           int graphic;
8624
8625           /* android is moving diagonally */
8626
8627           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8628
8629           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8630           GfxElement[x][y] = EL_EMC_ANDROID;
8631           GfxAction[x][y] = ACTION_SHRINKING;
8632           GfxDir[x][y] = diagonal_move_dir;
8633           ChangeDelay[x][y] = change_delay;
8634
8635           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8636                                    GfxDir[x][y]);
8637
8638           DrawLevelGraphicAnimation(x, y, graphic);
8639           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8640
8641           if (Feld[newx][newy] == EL_ACID)
8642           {
8643             SplashAcid(newx, newy);
8644
8645             return;
8646           }
8647
8648           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8649
8650           Store[newx][newy] = EL_EMC_ANDROID;
8651           GfxElement[newx][newy] = EL_EMC_ANDROID;
8652           GfxAction[newx][newy] = ACTION_GROWING;
8653           GfxDir[newx][newy] = diagonal_move_dir;
8654           ChangeDelay[newx][newy] = change_delay;
8655
8656           graphic = el_act_dir2img(GfxElement[newx][newy],
8657                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8658
8659           DrawLevelGraphicAnimation(newx, newy, graphic);
8660           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8661
8662           return;
8663         }
8664         else
8665         {
8666           Feld[newx][newy] = EL_EMPTY;
8667           TEST_DrawLevelField(newx, newy);
8668
8669           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8670         }
8671       }
8672       else if (!IS_FREE(newx, newy))
8673       {
8674 #if 0
8675         if (IS_PLAYER(x, y))
8676           DrawPlayerField(x, y);
8677         else
8678           TEST_DrawLevelField(x, y);
8679 #endif
8680
8681         return;
8682       }
8683     }
8684     else if (IS_CUSTOM_ELEMENT(element) &&
8685              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8686     {
8687 #if 1
8688       if (!DigFieldByCE(newx, newy, element))
8689         return;
8690 #else
8691       int new_element = Feld[newx][newy];
8692
8693       if (!IS_FREE(newx, newy))
8694       {
8695         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8696                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8697                       ACTION_BREAKING);
8698
8699         /* no element can dig solid indestructible elements */
8700         if (IS_INDESTRUCTIBLE(new_element) &&
8701             !IS_DIGGABLE(new_element) &&
8702             !IS_COLLECTIBLE(new_element))
8703           return;
8704
8705         if (AmoebaNr[newx][newy] &&
8706             (new_element == EL_AMOEBA_FULL ||
8707              new_element == EL_BD_AMOEBA ||
8708              new_element == EL_AMOEBA_GROWING))
8709         {
8710           AmoebaCnt[AmoebaNr[newx][newy]]--;
8711           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8712         }
8713
8714         if (IS_MOVING(newx, newy))
8715           RemoveMovingField(newx, newy);
8716         else
8717         {
8718           RemoveField(newx, newy);
8719           TEST_DrawLevelField(newx, newy);
8720         }
8721
8722         /* if digged element was about to explode, prevent the explosion */
8723         ExplodeField[newx][newy] = EX_TYPE_NONE;
8724
8725         PlayLevelSoundAction(x, y, action);
8726       }
8727
8728       Store[newx][newy] = EL_EMPTY;
8729
8730 #if 1
8731       /* this makes it possible to leave the removed element again */
8732       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8733         Store[newx][newy] = new_element;
8734 #else
8735       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8736       {
8737         int move_leave_element = element_info[element].move_leave_element;
8738
8739         /* this makes it possible to leave the removed element again */
8740         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8741                              new_element : move_leave_element);
8742       }
8743 #endif
8744
8745 #endif
8746
8747       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8748       {
8749         RunnerVisit[x][y] = FrameCounter;
8750         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8751       }
8752     }
8753     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8754     {
8755       if (!IS_FREE(newx, newy))
8756       {
8757         if (IS_PLAYER(x, y))
8758           DrawPlayerField(x, y);
8759         else
8760           TEST_DrawLevelField(x, y);
8761
8762         return;
8763       }
8764       else
8765       {
8766         boolean wanna_flame = !RND(10);
8767         int dx = newx - x, dy = newy - y;
8768         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8769         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8770         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8771                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8772         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8773                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8774
8775         if ((wanna_flame ||
8776              IS_CLASSIC_ENEMY(element1) ||
8777              IS_CLASSIC_ENEMY(element2)) &&
8778             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8779             element1 != EL_FLAMES && element2 != EL_FLAMES)
8780         {
8781           ResetGfxAnimation(x, y);
8782           GfxAction[x][y] = ACTION_ATTACKING;
8783
8784           if (IS_PLAYER(x, y))
8785             DrawPlayerField(x, y);
8786           else
8787             TEST_DrawLevelField(x, y);
8788
8789           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8790
8791           MovDelay[x][y] = 50;
8792
8793           /* !!! */
8794 #if 0
8795           RemoveField(newx, newy);
8796 #endif
8797           Feld[newx][newy] = EL_FLAMES;
8798           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8799           {
8800 #if 0
8801             RemoveField(newx1, newy1);
8802 #endif
8803             Feld[newx1][newy1] = EL_FLAMES;
8804           }
8805           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8806           {
8807 #if 0
8808             RemoveField(newx2, newy2);
8809 #endif
8810             Feld[newx2][newy2] = EL_FLAMES;
8811           }
8812
8813           return;
8814         }
8815       }
8816     }
8817     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8818              Feld[newx][newy] == EL_DIAMOND)
8819     {
8820       if (IS_MOVING(newx, newy))
8821         RemoveMovingField(newx, newy);
8822       else
8823       {
8824         Feld[newx][newy] = EL_EMPTY;
8825         TEST_DrawLevelField(newx, newy);
8826       }
8827
8828       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8829     }
8830     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8831              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8832     {
8833       if (AmoebaNr[newx][newy])
8834       {
8835         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8836         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8837             Feld[newx][newy] == EL_BD_AMOEBA)
8838           AmoebaCnt[AmoebaNr[newx][newy]]--;
8839       }
8840
8841 #if 0
8842       /* !!! test !!! */
8843       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8844       {
8845         RemoveMovingField(newx, newy);
8846       }
8847 #else
8848       if (IS_MOVING(newx, newy))
8849       {
8850         RemoveMovingField(newx, newy);
8851       }
8852 #endif
8853       else
8854       {
8855         Feld[newx][newy] = EL_EMPTY;
8856         TEST_DrawLevelField(newx, newy);
8857       }
8858
8859       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8860     }
8861     else if ((element == EL_PACMAN || element == EL_MOLE)
8862              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8863     {
8864       if (AmoebaNr[newx][newy])
8865       {
8866         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8867         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8868             Feld[newx][newy] == EL_BD_AMOEBA)
8869           AmoebaCnt[AmoebaNr[newx][newy]]--;
8870       }
8871
8872       if (element == EL_MOLE)
8873       {
8874         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8875         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8876
8877         ResetGfxAnimation(x, y);
8878         GfxAction[x][y] = ACTION_DIGGING;
8879         TEST_DrawLevelField(x, y);
8880
8881         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8882
8883         return;                         /* wait for shrinking amoeba */
8884       }
8885       else      /* element == EL_PACMAN */
8886       {
8887         Feld[newx][newy] = EL_EMPTY;
8888         TEST_DrawLevelField(newx, newy);
8889         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8890       }
8891     }
8892     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8893              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8894               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8895     {
8896       /* wait for shrinking amoeba to completely disappear */
8897       return;
8898     }
8899     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8900     {
8901       /* object was running against a wall */
8902
8903       TurnRound(x, y);
8904
8905 #if 0
8906       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8907       if (move_pattern & MV_ANY_DIRECTION &&
8908           move_pattern == MovDir[x][y])
8909       {
8910         int blocking_element =
8911           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8912
8913         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8914                                  MovDir[x][y]);
8915
8916         element = Feld[x][y];   /* element might have changed */
8917       }
8918 #endif
8919
8920       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8921         DrawLevelElementAnimation(x, y, element);
8922
8923       if (DONT_TOUCH(element))
8924         TestIfBadThingTouchesPlayer(x, y);
8925
8926       return;
8927     }
8928
8929     InitMovingField(x, y, MovDir[x][y]);
8930
8931     PlayLevelSoundAction(x, y, ACTION_MOVING);
8932   }
8933
8934   if (MovDir[x][y])
8935     ContinueMoving(x, y);
8936 }
8937
8938 void ContinueMoving(int x, int y)
8939 {
8940   int element = Feld[x][y];
8941   struct ElementInfo *ei = &element_info[element];
8942   int direction = MovDir[x][y];
8943   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8944   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8945   int newx = x + dx, newy = y + dy;
8946   int stored = Store[x][y];
8947   int stored_new = Store[newx][newy];
8948   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8949   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8950   boolean last_line = (newy == lev_fieldy - 1);
8951
8952   MovPos[x][y] += getElementMoveStepsize(x, y);
8953
8954   if (pushed_by_player) /* special case: moving object pushed by player */
8955     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8956
8957   if (ABS(MovPos[x][y]) < TILEX)
8958   {
8959 #if 0
8960     int ee = Feld[x][y];
8961     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8962     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8963
8964     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8965            x, y, ABS(MovPos[x][y]),
8966            ee, gg, ff,
8967            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8968 #endif
8969
8970     TEST_DrawLevelField(x, y);
8971
8972     return;     /* element is still moving */
8973   }
8974
8975   /* element reached destination field */
8976
8977   Feld[x][y] = EL_EMPTY;
8978   Feld[newx][newy] = element;
8979   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8980
8981   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8982   {
8983     element = Feld[newx][newy] = EL_ACID;
8984   }
8985   else if (element == EL_MOLE)
8986   {
8987     Feld[x][y] = EL_SAND;
8988
8989     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8990   }
8991   else if (element == EL_QUICKSAND_FILLING)
8992   {
8993     element = Feld[newx][newy] = get_next_element(element);
8994     Store[newx][newy] = Store[x][y];
8995   }
8996   else if (element == EL_QUICKSAND_EMPTYING)
8997   {
8998     Feld[x][y] = get_next_element(element);
8999     element = Feld[newx][newy] = Store[x][y];
9000   }
9001   else if (element == EL_QUICKSAND_FAST_FILLING)
9002   {
9003     element = Feld[newx][newy] = get_next_element(element);
9004     Store[newx][newy] = Store[x][y];
9005   }
9006   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9007   {
9008     Feld[x][y] = get_next_element(element);
9009     element = Feld[newx][newy] = Store[x][y];
9010   }
9011   else if (element == EL_MAGIC_WALL_FILLING)
9012   {
9013     element = Feld[newx][newy] = get_next_element(element);
9014     if (!game.magic_wall_active)
9015       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9016     Store[newx][newy] = Store[x][y];
9017   }
9018   else if (element == EL_MAGIC_WALL_EMPTYING)
9019   {
9020     Feld[x][y] = get_next_element(element);
9021     if (!game.magic_wall_active)
9022       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9023     element = Feld[newx][newy] = Store[x][y];
9024
9025 #if USE_NEW_CUSTOM_VALUE
9026     InitField(newx, newy, FALSE);
9027 #endif
9028   }
9029   else if (element == EL_BD_MAGIC_WALL_FILLING)
9030   {
9031     element = Feld[newx][newy] = get_next_element(element);
9032     if (!game.magic_wall_active)
9033       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9034     Store[newx][newy] = Store[x][y];
9035   }
9036   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9037   {
9038     Feld[x][y] = get_next_element(element);
9039     if (!game.magic_wall_active)
9040       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9041     element = Feld[newx][newy] = Store[x][y];
9042
9043 #if USE_NEW_CUSTOM_VALUE
9044     InitField(newx, newy, FALSE);
9045 #endif
9046   }
9047   else if (element == EL_DC_MAGIC_WALL_FILLING)
9048   {
9049     element = Feld[newx][newy] = get_next_element(element);
9050     if (!game.magic_wall_active)
9051       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9052     Store[newx][newy] = Store[x][y];
9053   }
9054   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9055   {
9056     Feld[x][y] = get_next_element(element);
9057     if (!game.magic_wall_active)
9058       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9059     element = Feld[newx][newy] = Store[x][y];
9060
9061 #if USE_NEW_CUSTOM_VALUE
9062     InitField(newx, newy, FALSE);
9063 #endif
9064   }
9065   else if (element == EL_AMOEBA_DROPPING)
9066   {
9067     Feld[x][y] = get_next_element(element);
9068     element = Feld[newx][newy] = Store[x][y];
9069   }
9070   else if (element == EL_SOKOBAN_OBJECT)
9071   {
9072     if (Back[x][y])
9073       Feld[x][y] = Back[x][y];
9074
9075     if (Back[newx][newy])
9076       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9077
9078     Back[x][y] = Back[newx][newy] = 0;
9079   }
9080
9081   Store[x][y] = EL_EMPTY;
9082   MovPos[x][y] = 0;
9083   MovDir[x][y] = 0;
9084   MovDelay[x][y] = 0;
9085
9086   MovDelay[newx][newy] = 0;
9087
9088   if (CAN_CHANGE_OR_HAS_ACTION(element))
9089   {
9090     /* copy element change control values to new field */
9091     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9092     ChangePage[newx][newy]  = ChangePage[x][y];
9093     ChangeCount[newx][newy] = ChangeCount[x][y];
9094     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9095   }
9096
9097 #if USE_NEW_CUSTOM_VALUE
9098   CustomValue[newx][newy] = CustomValue[x][y];
9099 #endif
9100
9101   ChangeDelay[x][y] = 0;
9102   ChangePage[x][y] = -1;
9103   ChangeCount[x][y] = 0;
9104   ChangeEvent[x][y] = -1;
9105
9106 #if USE_NEW_CUSTOM_VALUE
9107   CustomValue[x][y] = 0;
9108 #endif
9109
9110   /* copy animation control values to new field */
9111   GfxFrame[newx][newy]  = GfxFrame[x][y];
9112   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9113   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9114   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9115
9116   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9117
9118   /* some elements can leave other elements behind after moving */
9119 #if 1
9120   if (ei->move_leave_element != EL_EMPTY &&
9121       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9122       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9123 #else
9124   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9125       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9126       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9127 #endif
9128   {
9129     int move_leave_element = ei->move_leave_element;
9130
9131 #if 1
9132 #if 1
9133     /* this makes it possible to leave the removed element again */
9134     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9135       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9136 #else
9137     /* this makes it possible to leave the removed element again */
9138     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9139       move_leave_element = stored;
9140 #endif
9141 #else
9142     /* this makes it possible to leave the removed element again */
9143     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9144         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9145       move_leave_element = stored;
9146 #endif
9147
9148     Feld[x][y] = move_leave_element;
9149
9150     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9151       MovDir[x][y] = direction;
9152
9153     InitField(x, y, FALSE);
9154
9155     if (GFX_CRUMBLED(Feld[x][y]))
9156       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9157
9158     if (ELEM_IS_PLAYER(move_leave_element))
9159       RelocatePlayer(x, y, move_leave_element);
9160   }
9161
9162   /* do this after checking for left-behind element */
9163   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9164
9165   if (!CAN_MOVE(element) ||
9166       (CAN_FALL(element) && direction == MV_DOWN &&
9167        (element == EL_SPRING ||
9168         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9169         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9170     GfxDir[x][y] = MovDir[newx][newy] = 0;
9171
9172   TEST_DrawLevelField(x, y);
9173   TEST_DrawLevelField(newx, newy);
9174
9175   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9176
9177   /* prevent pushed element from moving on in pushed direction */
9178   if (pushed_by_player && CAN_MOVE(element) &&
9179       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9180       !(element_info[element].move_pattern & direction))
9181     TurnRound(newx, newy);
9182
9183   /* prevent elements on conveyor belt from moving on in last direction */
9184   if (pushed_by_conveyor && CAN_FALL(element) &&
9185       direction & MV_HORIZONTAL)
9186     MovDir[newx][newy] = 0;
9187
9188   if (!pushed_by_player)
9189   {
9190     int nextx = newx + dx, nexty = newy + dy;
9191     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9192
9193     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9194
9195     if (CAN_FALL(element) && direction == MV_DOWN)
9196       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9197
9198     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9199       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9200
9201 #if USE_FIX_IMPACT_COLLISION
9202     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9203       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9204 #endif
9205   }
9206
9207   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9208   {
9209     TestIfBadThingTouchesPlayer(newx, newy);
9210     TestIfBadThingTouchesFriend(newx, newy);
9211
9212     if (!IS_CUSTOM_ELEMENT(element))
9213       TestIfBadThingTouchesOtherBadThing(newx, newy);
9214   }
9215   else if (element == EL_PENGUIN)
9216     TestIfFriendTouchesBadThing(newx, newy);
9217
9218   if (DONT_GET_HIT_BY(element))
9219   {
9220     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9221   }
9222
9223   /* give the player one last chance (one more frame) to move away */
9224   if (CAN_FALL(element) && direction == MV_DOWN &&
9225       (last_line || (!IS_FREE(x, newy + 1) &&
9226                      (!IS_PLAYER(x, newy + 1) ||
9227                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9228     Impact(x, newy);
9229
9230   if (pushed_by_player && !game.use_change_when_pushing_bug)
9231   {
9232     int push_side = MV_DIR_OPPOSITE(direction);
9233     struct PlayerInfo *player = PLAYERINFO(x, y);
9234
9235     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9236                                player->index_bit, push_side);
9237     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9238                                         player->index_bit, push_side);
9239   }
9240
9241   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9242     MovDelay[newx][newy] = 1;
9243
9244   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9245
9246   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9247
9248 #if 0
9249   if (ChangePage[newx][newy] != -1)             /* delayed change */
9250   {
9251     int page = ChangePage[newx][newy];
9252     struct ElementChangeInfo *change = &ei->change_page[page];
9253
9254     ChangePage[newx][newy] = -1;
9255
9256     if (change->can_change)
9257     {
9258       if (ChangeElement(newx, newy, element, page))
9259       {
9260         if (change->post_change_function)
9261           change->post_change_function(newx, newy);
9262       }
9263     }
9264
9265     if (change->has_action)
9266       ExecuteCustomElementAction(newx, newy, element, page);
9267   }
9268 #endif
9269
9270   TestIfElementHitsCustomElement(newx, newy, direction);
9271   TestIfPlayerTouchesCustomElement(newx, newy);
9272   TestIfElementTouchesCustomElement(newx, newy);
9273
9274   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9275       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9276     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9277                              MV_DIR_OPPOSITE(direction));
9278 }
9279
9280 int AmoebeNachbarNr(int ax, int ay)
9281 {
9282   int i;
9283   int element = Feld[ax][ay];
9284   int group_nr = 0;
9285   static int xy[4][2] =
9286   {
9287     { 0, -1 },
9288     { -1, 0 },
9289     { +1, 0 },
9290     { 0, +1 }
9291   };
9292
9293   for (i = 0; i < NUM_DIRECTIONS; i++)
9294   {
9295     int x = ax + xy[i][0];
9296     int y = ay + xy[i][1];
9297
9298     if (!IN_LEV_FIELD(x, y))
9299       continue;
9300
9301     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9302       group_nr = AmoebaNr[x][y];
9303   }
9304
9305   return group_nr;
9306 }
9307
9308 void AmoebenVereinigen(int ax, int ay)
9309 {
9310   int i, x, y, xx, yy;
9311   int new_group_nr = AmoebaNr[ax][ay];
9312   static int xy[4][2] =
9313   {
9314     { 0, -1 },
9315     { -1, 0 },
9316     { +1, 0 },
9317     { 0, +1 }
9318   };
9319
9320   if (new_group_nr == 0)
9321     return;
9322
9323   for (i = 0; i < NUM_DIRECTIONS; i++)
9324   {
9325     x = ax + xy[i][0];
9326     y = ay + xy[i][1];
9327
9328     if (!IN_LEV_FIELD(x, y))
9329       continue;
9330
9331     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9332          Feld[x][y] == EL_BD_AMOEBA ||
9333          Feld[x][y] == EL_AMOEBA_DEAD) &&
9334         AmoebaNr[x][y] != new_group_nr)
9335     {
9336       int old_group_nr = AmoebaNr[x][y];
9337
9338       if (old_group_nr == 0)
9339         return;
9340
9341       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9342       AmoebaCnt[old_group_nr] = 0;
9343       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9344       AmoebaCnt2[old_group_nr] = 0;
9345
9346       SCAN_PLAYFIELD(xx, yy)
9347       {
9348         if (AmoebaNr[xx][yy] == old_group_nr)
9349           AmoebaNr[xx][yy] = new_group_nr;
9350       }
9351     }
9352   }
9353 }
9354
9355 void AmoebeUmwandeln(int ax, int ay)
9356 {
9357   int i, x, y;
9358
9359   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9360   {
9361     int group_nr = AmoebaNr[ax][ay];
9362
9363 #ifdef DEBUG
9364     if (group_nr == 0)
9365     {
9366       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9367       printf("AmoebeUmwandeln(): This should never happen!\n");
9368       return;
9369     }
9370 #endif
9371
9372     SCAN_PLAYFIELD(x, y)
9373     {
9374       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9375       {
9376         AmoebaNr[x][y] = 0;
9377         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9378       }
9379     }
9380
9381     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9382                             SND_AMOEBA_TURNING_TO_GEM :
9383                             SND_AMOEBA_TURNING_TO_ROCK));
9384     Bang(ax, ay);
9385   }
9386   else
9387   {
9388     static int xy[4][2] =
9389     {
9390       { 0, -1 },
9391       { -1, 0 },
9392       { +1, 0 },
9393       { 0, +1 }
9394     };
9395
9396     for (i = 0; i < NUM_DIRECTIONS; i++)
9397     {
9398       x = ax + xy[i][0];
9399       y = ay + xy[i][1];
9400
9401       if (!IN_LEV_FIELD(x, y))
9402         continue;
9403
9404       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9405       {
9406         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9407                               SND_AMOEBA_TURNING_TO_GEM :
9408                               SND_AMOEBA_TURNING_TO_ROCK));
9409         Bang(x, y);
9410       }
9411     }
9412   }
9413 }
9414
9415 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9416 {
9417   int x, y;
9418   int group_nr = AmoebaNr[ax][ay];
9419   boolean done = FALSE;
9420
9421 #ifdef DEBUG
9422   if (group_nr == 0)
9423   {
9424     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9425     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9426     return;
9427   }
9428 #endif
9429
9430   SCAN_PLAYFIELD(x, y)
9431   {
9432     if (AmoebaNr[x][y] == group_nr &&
9433         (Feld[x][y] == EL_AMOEBA_DEAD ||
9434          Feld[x][y] == EL_BD_AMOEBA ||
9435          Feld[x][y] == EL_AMOEBA_GROWING))
9436     {
9437       AmoebaNr[x][y] = 0;
9438       Feld[x][y] = new_element;
9439       InitField(x, y, FALSE);
9440       TEST_DrawLevelField(x, y);
9441       done = TRUE;
9442     }
9443   }
9444
9445   if (done)
9446     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9447                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9448                             SND_BD_AMOEBA_TURNING_TO_GEM));
9449 }
9450
9451 void AmoebeWaechst(int x, int y)
9452 {
9453   static unsigned long sound_delay = 0;
9454   static unsigned long sound_delay_value = 0;
9455
9456   if (!MovDelay[x][y])          /* start new growing cycle */
9457   {
9458     MovDelay[x][y] = 7;
9459
9460     if (DelayReached(&sound_delay, sound_delay_value))
9461     {
9462       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9463       sound_delay_value = 30;
9464     }
9465   }
9466
9467   if (MovDelay[x][y])           /* wait some time before growing bigger */
9468   {
9469     MovDelay[x][y]--;
9470     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9471     {
9472       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9473                                            6 - MovDelay[x][y]);
9474
9475       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9476     }
9477
9478     if (!MovDelay[x][y])
9479     {
9480       Feld[x][y] = Store[x][y];
9481       Store[x][y] = 0;
9482       TEST_DrawLevelField(x, y);
9483     }
9484   }
9485 }
9486
9487 void AmoebaDisappearing(int x, int y)
9488 {
9489   static unsigned long sound_delay = 0;
9490   static unsigned long sound_delay_value = 0;
9491
9492   if (!MovDelay[x][y])          /* start new shrinking cycle */
9493   {
9494     MovDelay[x][y] = 7;
9495
9496     if (DelayReached(&sound_delay, sound_delay_value))
9497       sound_delay_value = 30;
9498   }
9499
9500   if (MovDelay[x][y])           /* wait some time before shrinking */
9501   {
9502     MovDelay[x][y]--;
9503     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9504     {
9505       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9506                                            6 - MovDelay[x][y]);
9507
9508       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9509     }
9510
9511     if (!MovDelay[x][y])
9512     {
9513       Feld[x][y] = EL_EMPTY;
9514       TEST_DrawLevelField(x, y);
9515
9516       /* don't let mole enter this field in this cycle;
9517          (give priority to objects falling to this field from above) */
9518       Stop[x][y] = TRUE;
9519     }
9520   }
9521 }
9522
9523 void AmoebeAbleger(int ax, int ay)
9524 {
9525   int i;
9526   int element = Feld[ax][ay];
9527   int graphic = el2img(element);
9528   int newax = ax, neway = ay;
9529   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9530   static int xy[4][2] =
9531   {
9532     { 0, -1 },
9533     { -1, 0 },
9534     { +1, 0 },
9535     { 0, +1 }
9536   };
9537
9538   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9539   {
9540     Feld[ax][ay] = EL_AMOEBA_DEAD;
9541     TEST_DrawLevelField(ax, ay);
9542     return;
9543   }
9544
9545   if (IS_ANIMATED(graphic))
9546     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9547
9548   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9549     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9550
9551   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9552   {
9553     MovDelay[ax][ay]--;
9554     if (MovDelay[ax][ay])
9555       return;
9556   }
9557
9558   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9559   {
9560     int start = RND(4);
9561     int x = ax + xy[start][0];
9562     int y = ay + xy[start][1];
9563
9564     if (!IN_LEV_FIELD(x, y))
9565       return;
9566
9567     if (IS_FREE(x, y) ||
9568         CAN_GROW_INTO(Feld[x][y]) ||
9569         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9570         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9571     {
9572       newax = x;
9573       neway = y;
9574     }
9575
9576     if (newax == ax && neway == ay)
9577       return;
9578   }
9579   else                          /* normal or "filled" (BD style) amoeba */
9580   {
9581     int start = RND(4);
9582     boolean waiting_for_player = FALSE;
9583
9584     for (i = 0; i < NUM_DIRECTIONS; i++)
9585     {
9586       int j = (start + i) % 4;
9587       int x = ax + xy[j][0];
9588       int y = ay + xy[j][1];
9589
9590       if (!IN_LEV_FIELD(x, y))
9591         continue;
9592
9593       if (IS_FREE(x, y) ||
9594           CAN_GROW_INTO(Feld[x][y]) ||
9595           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9596           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9597       {
9598         newax = x;
9599         neway = y;
9600         break;
9601       }
9602       else if (IS_PLAYER(x, y))
9603         waiting_for_player = TRUE;
9604     }
9605
9606     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9607     {
9608       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9609       {
9610         Feld[ax][ay] = EL_AMOEBA_DEAD;
9611         TEST_DrawLevelField(ax, ay);
9612         AmoebaCnt[AmoebaNr[ax][ay]]--;
9613
9614         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9615         {
9616           if (element == EL_AMOEBA_FULL)
9617             AmoebeUmwandeln(ax, ay);
9618           else if (element == EL_BD_AMOEBA)
9619             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9620         }
9621       }
9622       return;
9623     }
9624     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9625     {
9626       /* amoeba gets larger by growing in some direction */
9627
9628       int new_group_nr = AmoebaNr[ax][ay];
9629
9630 #ifdef DEBUG
9631   if (new_group_nr == 0)
9632   {
9633     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9634     printf("AmoebeAbleger(): This should never happen!\n");
9635     return;
9636   }
9637 #endif
9638
9639       AmoebaNr[newax][neway] = new_group_nr;
9640       AmoebaCnt[new_group_nr]++;
9641       AmoebaCnt2[new_group_nr]++;
9642
9643       /* if amoeba touches other amoeba(s) after growing, unify them */
9644       AmoebenVereinigen(newax, neway);
9645
9646       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9647       {
9648         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9649         return;
9650       }
9651     }
9652   }
9653
9654   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9655       (neway == lev_fieldy - 1 && newax != ax))
9656   {
9657     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9658     Store[newax][neway] = element;
9659   }
9660   else if (neway == ay || element == EL_EMC_DRIPPER)
9661   {
9662     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9663
9664     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9665   }
9666   else
9667   {
9668     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9669     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9670     Store[ax][ay] = EL_AMOEBA_DROP;
9671     ContinueMoving(ax, ay);
9672     return;
9673   }
9674
9675   TEST_DrawLevelField(newax, neway);
9676 }
9677
9678 void Life(int ax, int ay)
9679 {
9680   int x1, y1, x2, y2;
9681   int life_time = 40;
9682   int element = Feld[ax][ay];
9683   int graphic = el2img(element);
9684   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9685                          level.biomaze);
9686   boolean changed = FALSE;
9687
9688   if (IS_ANIMATED(graphic))
9689     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9690
9691   if (Stop[ax][ay])
9692     return;
9693
9694   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9695     MovDelay[ax][ay] = life_time;
9696
9697   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9698   {
9699     MovDelay[ax][ay]--;
9700     if (MovDelay[ax][ay])
9701       return;
9702   }
9703
9704   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9705   {
9706     int xx = ax+x1, yy = ay+y1;
9707     int nachbarn = 0;
9708
9709     if (!IN_LEV_FIELD(xx, yy))
9710       continue;
9711
9712     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9713     {
9714       int x = xx+x2, y = yy+y2;
9715
9716       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9717         continue;
9718
9719       if (((Feld[x][y] == element ||
9720             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9721            !Stop[x][y]) ||
9722           (IS_FREE(x, y) && Stop[x][y]))
9723         nachbarn++;
9724     }
9725
9726     if (xx == ax && yy == ay)           /* field in the middle */
9727     {
9728       if (nachbarn < life_parameter[0] ||
9729           nachbarn > life_parameter[1])
9730       {
9731         Feld[xx][yy] = EL_EMPTY;
9732         if (!Stop[xx][yy])
9733           TEST_DrawLevelField(xx, yy);
9734         Stop[xx][yy] = TRUE;
9735         changed = TRUE;
9736       }
9737     }
9738     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9739     {                                   /* free border field */
9740       if (nachbarn >= life_parameter[2] &&
9741           nachbarn <= life_parameter[3])
9742       {
9743         Feld[xx][yy] = element;
9744         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9745         if (!Stop[xx][yy])
9746           TEST_DrawLevelField(xx, yy);
9747         Stop[xx][yy] = TRUE;
9748         changed = TRUE;
9749       }
9750     }
9751   }
9752
9753   if (changed)
9754     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9755                    SND_GAME_OF_LIFE_GROWING);
9756 }
9757
9758 static void InitRobotWheel(int x, int y)
9759 {
9760   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9761 }
9762
9763 static void RunRobotWheel(int x, int y)
9764 {
9765   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9766 }
9767
9768 static void StopRobotWheel(int x, int y)
9769 {
9770   if (ZX == x && ZY == y)
9771   {
9772     ZX = ZY = -1;
9773
9774     game.robot_wheel_active = FALSE;
9775   }
9776 }
9777
9778 static void InitTimegateWheel(int x, int y)
9779 {
9780   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9781 }
9782
9783 static void RunTimegateWheel(int x, int y)
9784 {
9785   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9786 }
9787
9788 static void InitMagicBallDelay(int x, int y)
9789 {
9790 #if 1
9791   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9792 #else
9793   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9794 #endif
9795 }
9796
9797 static void ActivateMagicBall(int bx, int by)
9798 {
9799   int x, y;
9800
9801   if (level.ball_random)
9802   {
9803     int pos_border = RND(8);    /* select one of the eight border elements */
9804     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9805     int xx = pos_content % 3;
9806     int yy = pos_content / 3;
9807
9808     x = bx - 1 + xx;
9809     y = by - 1 + yy;
9810
9811     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9812       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9813   }
9814   else
9815   {
9816     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9817     {
9818       int xx = x - bx + 1;
9819       int yy = y - by + 1;
9820
9821       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9822         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9823     }
9824   }
9825
9826   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9827 }
9828
9829 void CheckExit(int x, int y)
9830 {
9831   if (local_player->gems_still_needed > 0 ||
9832       local_player->sokobanfields_still_needed > 0 ||
9833       local_player->lights_still_needed > 0)
9834   {
9835     int element = Feld[x][y];
9836     int graphic = el2img(element);
9837
9838     if (IS_ANIMATED(graphic))
9839       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9840
9841     return;
9842   }
9843
9844   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9845     return;
9846
9847   Feld[x][y] = EL_EXIT_OPENING;
9848
9849   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9850 }
9851
9852 void CheckExitEM(int x, int y)
9853 {
9854   if (local_player->gems_still_needed > 0 ||
9855       local_player->sokobanfields_still_needed > 0 ||
9856       local_player->lights_still_needed > 0)
9857   {
9858     int element = Feld[x][y];
9859     int graphic = el2img(element);
9860
9861     if (IS_ANIMATED(graphic))
9862       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9863
9864     return;
9865   }
9866
9867   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9868     return;
9869
9870   Feld[x][y] = EL_EM_EXIT_OPENING;
9871
9872   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9873 }
9874
9875 void CheckExitSteel(int x, int y)
9876 {
9877   if (local_player->gems_still_needed > 0 ||
9878       local_player->sokobanfields_still_needed > 0 ||
9879       local_player->lights_still_needed > 0)
9880   {
9881     int element = Feld[x][y];
9882     int graphic = el2img(element);
9883
9884     if (IS_ANIMATED(graphic))
9885       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9886
9887     return;
9888   }
9889
9890   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9891     return;
9892
9893   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9894
9895   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9896 }
9897
9898 void CheckExitSteelEM(int x, int y)
9899 {
9900   if (local_player->gems_still_needed > 0 ||
9901       local_player->sokobanfields_still_needed > 0 ||
9902       local_player->lights_still_needed > 0)
9903   {
9904     int element = Feld[x][y];
9905     int graphic = el2img(element);
9906
9907     if (IS_ANIMATED(graphic))
9908       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9909
9910     return;
9911   }
9912
9913   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9914     return;
9915
9916   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9917
9918   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9919 }
9920
9921 void CheckExitSP(int x, int y)
9922 {
9923   if (local_player->gems_still_needed > 0)
9924   {
9925     int element = Feld[x][y];
9926     int graphic = el2img(element);
9927
9928     if (IS_ANIMATED(graphic))
9929       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9930
9931     return;
9932   }
9933
9934   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9935     return;
9936
9937   Feld[x][y] = EL_SP_EXIT_OPENING;
9938
9939   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9940 }
9941
9942 static void CloseAllOpenTimegates()
9943 {
9944   int x, y;
9945
9946   SCAN_PLAYFIELD(x, y)
9947   {
9948     int element = Feld[x][y];
9949
9950     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9951     {
9952       Feld[x][y] = EL_TIMEGATE_CLOSING;
9953
9954       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9955     }
9956   }
9957 }
9958
9959 void DrawTwinkleOnField(int x, int y)
9960 {
9961   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9962     return;
9963
9964   if (Feld[x][y] == EL_BD_DIAMOND)
9965     return;
9966
9967   if (MovDelay[x][y] == 0)      /* next animation frame */
9968     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9969
9970   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9971   {
9972     MovDelay[x][y]--;
9973
9974     DrawLevelElementAnimation(x, y, Feld[x][y]);
9975
9976     if (MovDelay[x][y] != 0)
9977     {
9978       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9979                                            10 - MovDelay[x][y]);
9980
9981       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9982     }
9983   }
9984 }
9985
9986 void MauerWaechst(int x, int y)
9987 {
9988   int delay = 6;
9989
9990   if (!MovDelay[x][y])          /* next animation frame */
9991     MovDelay[x][y] = 3 * delay;
9992
9993   if (MovDelay[x][y])           /* wait some time before next frame */
9994   {
9995     MovDelay[x][y]--;
9996
9997     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9998     {
9999       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10000       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10001
10002       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10003     }
10004
10005     if (!MovDelay[x][y])
10006     {
10007       if (MovDir[x][y] == MV_LEFT)
10008       {
10009         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10010           TEST_DrawLevelField(x - 1, y);
10011       }
10012       else if (MovDir[x][y] == MV_RIGHT)
10013       {
10014         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10015           TEST_DrawLevelField(x + 1, y);
10016       }
10017       else if (MovDir[x][y] == MV_UP)
10018       {
10019         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10020           TEST_DrawLevelField(x, y - 1);
10021       }
10022       else
10023       {
10024         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10025           TEST_DrawLevelField(x, y + 1);
10026       }
10027
10028       Feld[x][y] = Store[x][y];
10029       Store[x][y] = 0;
10030       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10031       TEST_DrawLevelField(x, y);
10032     }
10033   }
10034 }
10035
10036 void MauerAbleger(int ax, int ay)
10037 {
10038   int element = Feld[ax][ay];
10039   int graphic = el2img(element);
10040   boolean oben_frei = FALSE, unten_frei = FALSE;
10041   boolean links_frei = FALSE, rechts_frei = FALSE;
10042   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10043   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10044   boolean new_wall = FALSE;
10045
10046   if (IS_ANIMATED(graphic))
10047     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10048
10049   if (!MovDelay[ax][ay])        /* start building new wall */
10050     MovDelay[ax][ay] = 6;
10051
10052   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10053   {
10054     MovDelay[ax][ay]--;
10055     if (MovDelay[ax][ay])
10056       return;
10057   }
10058
10059   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10060     oben_frei = TRUE;
10061   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10062     unten_frei = TRUE;
10063   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10064     links_frei = TRUE;
10065   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10066     rechts_frei = TRUE;
10067
10068   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10069       element == EL_EXPANDABLE_WALL_ANY)
10070   {
10071     if (oben_frei)
10072     {
10073       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10074       Store[ax][ay-1] = element;
10075       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10076       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10077         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10078                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10079       new_wall = TRUE;
10080     }
10081     if (unten_frei)
10082     {
10083       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10084       Store[ax][ay+1] = element;
10085       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10086       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10087         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10088                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10089       new_wall = TRUE;
10090     }
10091   }
10092
10093   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10094       element == EL_EXPANDABLE_WALL_ANY ||
10095       element == EL_EXPANDABLE_WALL ||
10096       element == EL_BD_EXPANDABLE_WALL)
10097   {
10098     if (links_frei)
10099     {
10100       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10101       Store[ax-1][ay] = element;
10102       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10103       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10104         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10105                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10106       new_wall = TRUE;
10107     }
10108
10109     if (rechts_frei)
10110     {
10111       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10112       Store[ax+1][ay] = element;
10113       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10114       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10115         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10116                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10117       new_wall = TRUE;
10118     }
10119   }
10120
10121   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10122     TEST_DrawLevelField(ax, ay);
10123
10124   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10125     oben_massiv = TRUE;
10126   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10127     unten_massiv = TRUE;
10128   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10129     links_massiv = TRUE;
10130   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10131     rechts_massiv = TRUE;
10132
10133   if (((oben_massiv && unten_massiv) ||
10134        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10135        element == EL_EXPANDABLE_WALL) &&
10136       ((links_massiv && rechts_massiv) ||
10137        element == EL_EXPANDABLE_WALL_VERTICAL))
10138     Feld[ax][ay] = EL_WALL;
10139
10140   if (new_wall)
10141     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10142 }
10143
10144 void MauerAblegerStahl(int ax, int ay)
10145 {
10146   int element = Feld[ax][ay];
10147   int graphic = el2img(element);
10148   boolean oben_frei = FALSE, unten_frei = FALSE;
10149   boolean links_frei = FALSE, rechts_frei = FALSE;
10150   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10151   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10152   boolean new_wall = FALSE;
10153
10154   if (IS_ANIMATED(graphic))
10155     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10156
10157   if (!MovDelay[ax][ay])        /* start building new wall */
10158     MovDelay[ax][ay] = 6;
10159
10160   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10161   {
10162     MovDelay[ax][ay]--;
10163     if (MovDelay[ax][ay])
10164       return;
10165   }
10166
10167   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10168     oben_frei = TRUE;
10169   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10170     unten_frei = TRUE;
10171   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10172     links_frei = TRUE;
10173   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10174     rechts_frei = TRUE;
10175
10176   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10177       element == EL_EXPANDABLE_STEELWALL_ANY)
10178   {
10179     if (oben_frei)
10180     {
10181       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10182       Store[ax][ay-1] = element;
10183       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10184       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10185         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10186                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10187       new_wall = TRUE;
10188     }
10189     if (unten_frei)
10190     {
10191       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10192       Store[ax][ay+1] = element;
10193       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10194       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10195         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10196                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10197       new_wall = TRUE;
10198     }
10199   }
10200
10201   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10202       element == EL_EXPANDABLE_STEELWALL_ANY)
10203   {
10204     if (links_frei)
10205     {
10206       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10207       Store[ax-1][ay] = element;
10208       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10209       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10210         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10211                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10212       new_wall = TRUE;
10213     }
10214
10215     if (rechts_frei)
10216     {
10217       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10218       Store[ax+1][ay] = element;
10219       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10220       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10221         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10222                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10223       new_wall = TRUE;
10224     }
10225   }
10226
10227   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10228     oben_massiv = TRUE;
10229   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10230     unten_massiv = TRUE;
10231   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10232     links_massiv = TRUE;
10233   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10234     rechts_massiv = TRUE;
10235
10236   if (((oben_massiv && unten_massiv) ||
10237        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10238       ((links_massiv && rechts_massiv) ||
10239        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10240     Feld[ax][ay] = EL_STEELWALL;
10241
10242   if (new_wall)
10243     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10244 }
10245
10246 void CheckForDragon(int x, int y)
10247 {
10248   int i, j;
10249   boolean dragon_found = FALSE;
10250   static int xy[4][2] =
10251   {
10252     { 0, -1 },
10253     { -1, 0 },
10254     { +1, 0 },
10255     { 0, +1 }
10256   };
10257
10258   for (i = 0; i < NUM_DIRECTIONS; i++)
10259   {
10260     for (j = 0; j < 4; j++)
10261     {
10262       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10263
10264       if (IN_LEV_FIELD(xx, yy) &&
10265           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10266       {
10267         if (Feld[xx][yy] == EL_DRAGON)
10268           dragon_found = TRUE;
10269       }
10270       else
10271         break;
10272     }
10273   }
10274
10275   if (!dragon_found)
10276   {
10277     for (i = 0; i < NUM_DIRECTIONS; i++)
10278     {
10279       for (j = 0; j < 3; j++)
10280       {
10281         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10282   
10283         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10284         {
10285           Feld[xx][yy] = EL_EMPTY;
10286           TEST_DrawLevelField(xx, yy);
10287         }
10288         else
10289           break;
10290       }
10291     }
10292   }
10293 }
10294
10295 static void InitBuggyBase(int x, int y)
10296 {
10297   int element = Feld[x][y];
10298   int activating_delay = FRAMES_PER_SECOND / 4;
10299
10300   ChangeDelay[x][y] =
10301     (element == EL_SP_BUGGY_BASE ?
10302      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10303      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10304      activating_delay :
10305      element == EL_SP_BUGGY_BASE_ACTIVE ?
10306      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10307 }
10308
10309 static void WarnBuggyBase(int x, int y)
10310 {
10311   int i;
10312   static int xy[4][2] =
10313   {
10314     { 0, -1 },
10315     { -1, 0 },
10316     { +1, 0 },
10317     { 0, +1 }
10318   };
10319
10320   for (i = 0; i < NUM_DIRECTIONS; i++)
10321   {
10322     int xx = x + xy[i][0];
10323     int yy = y + xy[i][1];
10324
10325     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10326     {
10327       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10328
10329       break;
10330     }
10331   }
10332 }
10333
10334 static void InitTrap(int x, int y)
10335 {
10336   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10337 }
10338
10339 static void ActivateTrap(int x, int y)
10340 {
10341   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10342 }
10343
10344 static void ChangeActiveTrap(int x, int y)
10345 {
10346   int graphic = IMG_TRAP_ACTIVE;
10347
10348   /* if new animation frame was drawn, correct crumbled sand border */
10349   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10350     TEST_DrawLevelFieldCrumbledSand(x, y);
10351 }
10352
10353 static int getSpecialActionElement(int element, int number, int base_element)
10354 {
10355   return (element != EL_EMPTY ? element :
10356           number != -1 ? base_element + number - 1 :
10357           EL_EMPTY);
10358 }
10359
10360 static int getModifiedActionNumber(int value_old, int operator, int operand,
10361                                    int value_min, int value_max)
10362 {
10363   int value_new = (operator == CA_MODE_SET      ? operand :
10364                    operator == CA_MODE_ADD      ? value_old + operand :
10365                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10366                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10367                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10368                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10369                    value_old);
10370
10371   return (value_new < value_min ? value_min :
10372           value_new > value_max ? value_max :
10373           value_new);
10374 }
10375
10376 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10377 {
10378   struct ElementInfo *ei = &element_info[element];
10379   struct ElementChangeInfo *change = &ei->change_page[page];
10380   int target_element = change->target_element;
10381   int action_type = change->action_type;
10382   int action_mode = change->action_mode;
10383   int action_arg = change->action_arg;
10384   int action_element = change->action_element;
10385   int i;
10386
10387   if (!change->has_action)
10388     return;
10389
10390   /* ---------- determine action paramater values -------------------------- */
10391
10392   int level_time_value =
10393     (level.time > 0 ? TimeLeft :
10394      TimePlayed);
10395
10396   int action_arg_element_raw =
10397     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10398      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10399      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10400      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10401      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10402      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10403      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10404      EL_EMPTY);
10405   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10406
10407 #if 0
10408   if (action_arg_element_raw == EL_GROUP_START)
10409     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10410 #endif
10411
10412   int action_arg_direction =
10413     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10414      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10415      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10416      change->actual_trigger_side :
10417      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10418      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10419      MV_NONE);
10420
10421   int action_arg_number_min =
10422     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10423      CA_ARG_MIN);
10424
10425   int action_arg_number_max =
10426     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10427      action_type == CA_SET_LEVEL_GEMS ? 999 :
10428      action_type == CA_SET_LEVEL_TIME ? 9999 :
10429      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10430      action_type == CA_SET_CE_VALUE ? 9999 :
10431      action_type == CA_SET_CE_SCORE ? 9999 :
10432      CA_ARG_MAX);
10433
10434   int action_arg_number_reset =
10435     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10436      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10437      action_type == CA_SET_LEVEL_TIME ? level.time :
10438      action_type == CA_SET_LEVEL_SCORE ? 0 :
10439 #if USE_NEW_CUSTOM_VALUE
10440      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10441 #else
10442      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10443 #endif
10444      action_type == CA_SET_CE_SCORE ? 0 :
10445      0);
10446
10447   int action_arg_number =
10448     (action_arg <= CA_ARG_MAX ? action_arg :
10449      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10450      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10451      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10452      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10453      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10454      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10455 #if USE_NEW_CUSTOM_VALUE
10456      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10457 #else
10458      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10459 #endif
10460      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10461      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10462      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10463      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10464      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10465      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10466      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10467      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10468      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10469      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10470      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10471      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10472      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10473      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10474      -1);
10475
10476   int action_arg_number_old =
10477     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10478      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10479      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10480      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10481      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10482      0);
10483
10484   int action_arg_number_new =
10485     getModifiedActionNumber(action_arg_number_old,
10486                             action_mode, action_arg_number,
10487                             action_arg_number_min, action_arg_number_max);
10488
10489 #if 1
10490   int trigger_player_bits =
10491     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10492      change->actual_trigger_player_bits : change->trigger_player);
10493 #else
10494   int trigger_player_bits =
10495     (change->actual_trigger_player >= EL_PLAYER_1 &&
10496      change->actual_trigger_player <= EL_PLAYER_4 ?
10497      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10498      PLAYER_BITS_ANY);
10499 #endif
10500
10501   int action_arg_player_bits =
10502     (action_arg >= CA_ARG_PLAYER_1 &&
10503      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10504      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10505      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10506      PLAYER_BITS_ANY);
10507
10508   /* ---------- execute action  -------------------------------------------- */
10509
10510   switch (action_type)
10511   {
10512     case CA_NO_ACTION:
10513     {
10514       return;
10515     }
10516
10517     /* ---------- level actions  ------------------------------------------- */
10518
10519     case CA_RESTART_LEVEL:
10520     {
10521       game.restart_level = TRUE;
10522
10523       break;
10524     }
10525
10526     case CA_SHOW_ENVELOPE:
10527     {
10528       int element = getSpecialActionElement(action_arg_element,
10529                                             action_arg_number, EL_ENVELOPE_1);
10530
10531       if (IS_ENVELOPE(element))
10532         local_player->show_envelope = element;
10533
10534       break;
10535     }
10536
10537     case CA_SET_LEVEL_TIME:
10538     {
10539       if (level.time > 0)       /* only modify limited time value */
10540       {
10541         TimeLeft = action_arg_number_new;
10542
10543 #if 1
10544         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10545
10546         DisplayGameControlValues();
10547 #else
10548         DrawGameValue_Time(TimeLeft);
10549 #endif
10550
10551         if (!TimeLeft && setup.time_limit)
10552           for (i = 0; i < MAX_PLAYERS; i++)
10553             KillPlayer(&stored_player[i]);
10554       }
10555
10556       break;
10557     }
10558
10559     case CA_SET_LEVEL_SCORE:
10560     {
10561       local_player->score = action_arg_number_new;
10562
10563 #if 1
10564       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10565
10566       DisplayGameControlValues();
10567 #else
10568       DrawGameValue_Score(local_player->score);
10569 #endif
10570
10571       break;
10572     }
10573
10574     case CA_SET_LEVEL_GEMS:
10575     {
10576       local_player->gems_still_needed = action_arg_number_new;
10577
10578 #if 1
10579       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10580
10581       DisplayGameControlValues();
10582 #else
10583       DrawGameValue_Emeralds(local_player->gems_still_needed);
10584 #endif
10585
10586       break;
10587     }
10588
10589 #if !USE_PLAYER_GRAVITY
10590     case CA_SET_LEVEL_GRAVITY:
10591     {
10592       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10593                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10594                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10595                       game.gravity);
10596       break;
10597     }
10598 #endif
10599
10600     case CA_SET_LEVEL_WIND:
10601     {
10602       game.wind_direction = action_arg_direction;
10603
10604       break;
10605     }
10606
10607     case CA_SET_LEVEL_RANDOM_SEED:
10608     {
10609 #if 1
10610       /* ensure that setting a new random seed while playing is predictable */
10611       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10612 #else
10613       InitRND(action_arg_number_new);
10614 #endif
10615
10616 #if 0
10617       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10618 #endif
10619
10620 #if 0
10621       {
10622         int i;
10623
10624         printf("::: ");
10625         for (i = 0; i < 9; i++)
10626           printf("%d, ", RND(2));
10627         printf("\n");
10628       }
10629 #endif
10630
10631       break;
10632     }
10633
10634     /* ---------- player actions  ------------------------------------------ */
10635
10636     case CA_MOVE_PLAYER:
10637     {
10638       /* automatically move to the next field in specified direction */
10639       for (i = 0; i < MAX_PLAYERS; i++)
10640         if (trigger_player_bits & (1 << i))
10641           stored_player[i].programmed_action = action_arg_direction;
10642
10643       break;
10644     }
10645
10646     case CA_EXIT_PLAYER:
10647     {
10648       for (i = 0; i < MAX_PLAYERS; i++)
10649         if (action_arg_player_bits & (1 << i))
10650           PlayerWins(&stored_player[i]);
10651
10652       break;
10653     }
10654
10655     case CA_KILL_PLAYER:
10656     {
10657       for (i = 0; i < MAX_PLAYERS; i++)
10658         if (action_arg_player_bits & (1 << i))
10659           KillPlayer(&stored_player[i]);
10660
10661       break;
10662     }
10663
10664     case CA_SET_PLAYER_KEYS:
10665     {
10666       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10667       int element = getSpecialActionElement(action_arg_element,
10668                                             action_arg_number, EL_KEY_1);
10669
10670       if (IS_KEY(element))
10671       {
10672         for (i = 0; i < MAX_PLAYERS; i++)
10673         {
10674           if (trigger_player_bits & (1 << i))
10675           {
10676             stored_player[i].key[KEY_NR(element)] = key_state;
10677
10678             DrawGameDoorValues();
10679           }
10680         }
10681       }
10682
10683       break;
10684     }
10685
10686     case CA_SET_PLAYER_SPEED:
10687     {
10688 #if 0
10689       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10690 #endif
10691
10692       for (i = 0; i < MAX_PLAYERS; i++)
10693       {
10694         if (trigger_player_bits & (1 << i))
10695         {
10696           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10697
10698           if (action_arg == CA_ARG_SPEED_FASTER &&
10699               stored_player[i].cannot_move)
10700           {
10701             action_arg_number = STEPSIZE_VERY_SLOW;
10702           }
10703           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10704                    action_arg == CA_ARG_SPEED_FASTER)
10705           {
10706             action_arg_number = 2;
10707             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10708                            CA_MODE_MULTIPLY);
10709           }
10710           else if (action_arg == CA_ARG_NUMBER_RESET)
10711           {
10712             action_arg_number = level.initial_player_stepsize[i];
10713           }
10714
10715           move_stepsize =
10716             getModifiedActionNumber(move_stepsize,
10717                                     action_mode,
10718                                     action_arg_number,
10719                                     action_arg_number_min,
10720                                     action_arg_number_max);
10721
10722           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10723         }
10724       }
10725
10726       break;
10727     }
10728
10729     case CA_SET_PLAYER_SHIELD:
10730     {
10731       for (i = 0; i < MAX_PLAYERS; i++)
10732       {
10733         if (trigger_player_bits & (1 << i))
10734         {
10735           if (action_arg == CA_ARG_SHIELD_OFF)
10736           {
10737             stored_player[i].shield_normal_time_left = 0;
10738             stored_player[i].shield_deadly_time_left = 0;
10739           }
10740           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10741           {
10742             stored_player[i].shield_normal_time_left = 999999;
10743           }
10744           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10745           {
10746             stored_player[i].shield_normal_time_left = 999999;
10747             stored_player[i].shield_deadly_time_left = 999999;
10748           }
10749         }
10750       }
10751
10752       break;
10753     }
10754
10755 #if USE_PLAYER_GRAVITY
10756     case CA_SET_PLAYER_GRAVITY:
10757     {
10758       for (i = 0; i < MAX_PLAYERS; i++)
10759       {
10760         if (trigger_player_bits & (1 << i))
10761         {
10762           stored_player[i].gravity =
10763             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10764              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10765              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10766              stored_player[i].gravity);
10767         }
10768       }
10769
10770       break;
10771     }
10772 #endif
10773
10774     case CA_SET_PLAYER_ARTWORK:
10775     {
10776       for (i = 0; i < MAX_PLAYERS; i++)
10777       {
10778         if (trigger_player_bits & (1 << i))
10779         {
10780           int artwork_element = action_arg_element;
10781
10782           if (action_arg == CA_ARG_ELEMENT_RESET)
10783             artwork_element =
10784               (level.use_artwork_element[i] ? level.artwork_element[i] :
10785                stored_player[i].element_nr);
10786
10787 #if USE_GFX_RESET_PLAYER_ARTWORK
10788           if (stored_player[i].artwork_element != artwork_element)
10789             stored_player[i].Frame = 0;
10790 #endif
10791
10792           stored_player[i].artwork_element = artwork_element;
10793
10794           SetPlayerWaiting(&stored_player[i], FALSE);
10795
10796           /* set number of special actions for bored and sleeping animation */
10797           stored_player[i].num_special_action_bored =
10798             get_num_special_action(artwork_element,
10799                                    ACTION_BORING_1, ACTION_BORING_LAST);
10800           stored_player[i].num_special_action_sleeping =
10801             get_num_special_action(artwork_element,
10802                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10803         }
10804       }
10805
10806       break;
10807     }
10808
10809     case CA_SET_PLAYER_INVENTORY:
10810     {
10811       for (i = 0; i < MAX_PLAYERS; i++)
10812       {
10813         struct PlayerInfo *player = &stored_player[i];
10814         int j, k;
10815
10816         if (trigger_player_bits & (1 << i))
10817         {
10818           int inventory_element = action_arg_element;
10819
10820           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10821               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10822               action_arg == CA_ARG_ELEMENT_ACTION)
10823           {
10824             int element = inventory_element;
10825             int collect_count = element_info[element].collect_count_initial;
10826
10827             if (!IS_CUSTOM_ELEMENT(element))
10828               collect_count = 1;
10829
10830             if (collect_count == 0)
10831               player->inventory_infinite_element = element;
10832             else
10833               for (k = 0; k < collect_count; k++)
10834                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10835                   player->inventory_element[player->inventory_size++] =
10836                     element;
10837           }
10838           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10839                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10840                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10841           {
10842             if (player->inventory_infinite_element != EL_UNDEFINED &&
10843                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10844                                      action_arg_element_raw))
10845               player->inventory_infinite_element = EL_UNDEFINED;
10846
10847             for (k = 0, j = 0; j < player->inventory_size; j++)
10848             {
10849               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10850                                         action_arg_element_raw))
10851                 player->inventory_element[k++] = player->inventory_element[j];
10852             }
10853
10854             player->inventory_size = k;
10855           }
10856           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10857           {
10858             if (player->inventory_size > 0)
10859             {
10860               for (j = 0; j < player->inventory_size - 1; j++)
10861                 player->inventory_element[j] = player->inventory_element[j + 1];
10862
10863               player->inventory_size--;
10864             }
10865           }
10866           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10867           {
10868             if (player->inventory_size > 0)
10869               player->inventory_size--;
10870           }
10871           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10872           {
10873             player->inventory_infinite_element = EL_UNDEFINED;
10874             player->inventory_size = 0;
10875           }
10876           else if (action_arg == CA_ARG_INVENTORY_RESET)
10877           {
10878             player->inventory_infinite_element = EL_UNDEFINED;
10879             player->inventory_size = 0;
10880
10881             if (level.use_initial_inventory[i])
10882             {
10883               for (j = 0; j < level.initial_inventory_size[i]; j++)
10884               {
10885                 int element = level.initial_inventory_content[i][j];
10886                 int collect_count = element_info[element].collect_count_initial;
10887
10888                 if (!IS_CUSTOM_ELEMENT(element))
10889                   collect_count = 1;
10890
10891                 if (collect_count == 0)
10892                   player->inventory_infinite_element = element;
10893                 else
10894                   for (k = 0; k < collect_count; k++)
10895                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10896                       player->inventory_element[player->inventory_size++] =
10897                         element;
10898               }
10899             }
10900           }
10901         }
10902       }
10903
10904       break;
10905     }
10906
10907     /* ---------- CE actions  ---------------------------------------------- */
10908
10909     case CA_SET_CE_VALUE:
10910     {
10911 #if USE_NEW_CUSTOM_VALUE
10912       int last_ce_value = CustomValue[x][y];
10913
10914       CustomValue[x][y] = action_arg_number_new;
10915
10916       if (CustomValue[x][y] != last_ce_value)
10917       {
10918         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10919         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10920
10921         if (CustomValue[x][y] == 0)
10922         {
10923           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10924           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10925         }
10926       }
10927 #endif
10928
10929       break;
10930     }
10931
10932     case CA_SET_CE_SCORE:
10933     {
10934 #if USE_NEW_CUSTOM_VALUE
10935       int last_ce_score = ei->collect_score;
10936
10937       ei->collect_score = action_arg_number_new;
10938
10939       if (ei->collect_score != last_ce_score)
10940       {
10941         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10942         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10943
10944         if (ei->collect_score == 0)
10945         {
10946           int xx, yy;
10947
10948           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10949           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10950
10951           /*
10952             This is a very special case that seems to be a mixture between
10953             CheckElementChange() and CheckTriggeredElementChange(): while
10954             the first one only affects single elements that are triggered
10955             directly, the second one affects multiple elements in the playfield
10956             that are triggered indirectly by another element. This is a third
10957             case: Changing the CE score always affects multiple identical CEs,
10958             so every affected CE must be checked, not only the single CE for
10959             which the CE score was changed in the first place (as every instance
10960             of that CE shares the same CE score, and therefore also can change)!
10961           */
10962           SCAN_PLAYFIELD(xx, yy)
10963           {
10964             if (Feld[xx][yy] == element)
10965               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10966                                  CE_SCORE_GETS_ZERO);
10967           }
10968         }
10969       }
10970 #endif
10971
10972       break;
10973     }
10974
10975     case CA_SET_CE_ARTWORK:
10976     {
10977       int artwork_element = action_arg_element;
10978       boolean reset_frame = FALSE;
10979       int xx, yy;
10980
10981       if (action_arg == CA_ARG_ELEMENT_RESET)
10982         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10983                            element);
10984
10985       if (ei->gfx_element != artwork_element)
10986         reset_frame = TRUE;
10987
10988       ei->gfx_element = artwork_element;
10989
10990       SCAN_PLAYFIELD(xx, yy)
10991       {
10992         if (Feld[xx][yy] == element)
10993         {
10994           if (reset_frame)
10995           {
10996             ResetGfxAnimation(xx, yy);
10997             ResetRandomAnimationValue(xx, yy);
10998           }
10999
11000           TEST_DrawLevelField(xx, yy);
11001         }
11002       }
11003
11004       break;
11005     }
11006
11007     /* ---------- engine actions  ------------------------------------------ */
11008
11009     case CA_SET_ENGINE_SCAN_MODE:
11010     {
11011       InitPlayfieldScanMode(action_arg);
11012
11013       break;
11014     }
11015
11016     default:
11017       break;
11018   }
11019 }
11020
11021 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11022 {
11023   int old_element = Feld[x][y];
11024   int new_element = GetElementFromGroupElement(element);
11025   int previous_move_direction = MovDir[x][y];
11026 #if USE_NEW_CUSTOM_VALUE
11027   int last_ce_value = CustomValue[x][y];
11028 #endif
11029   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11030   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11031   boolean add_player_onto_element = (new_element_is_player &&
11032 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11033                                      /* this breaks SnakeBite when a snake is
11034                                         halfway through a door that closes */
11035                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11036                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11037 #endif
11038                                      IS_WALKABLE(old_element));
11039
11040 #if 0
11041   /* check if element under the player changes from accessible to unaccessible
11042      (needed for special case of dropping element which then changes) */
11043   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11044       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11045   {
11046     Bang(x, y);
11047
11048     return;
11049   }
11050 #endif
11051
11052   if (!add_player_onto_element)
11053   {
11054     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11055       RemoveMovingField(x, y);
11056     else
11057       RemoveField(x, y);
11058
11059     Feld[x][y] = new_element;
11060
11061 #if !USE_GFX_RESET_GFX_ANIMATION
11062     ResetGfxAnimation(x, y);
11063     ResetRandomAnimationValue(x, y);
11064 #endif
11065
11066     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11067       MovDir[x][y] = previous_move_direction;
11068
11069 #if USE_NEW_CUSTOM_VALUE
11070     if (element_info[new_element].use_last_ce_value)
11071       CustomValue[x][y] = last_ce_value;
11072 #endif
11073
11074     InitField_WithBug1(x, y, FALSE);
11075
11076     new_element = Feld[x][y];   /* element may have changed */
11077
11078 #if USE_GFX_RESET_GFX_ANIMATION
11079     ResetGfxAnimation(x, y);
11080     ResetRandomAnimationValue(x, y);
11081 #endif
11082
11083     TEST_DrawLevelField(x, y);
11084
11085     if (GFX_CRUMBLED(new_element))
11086       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11087   }
11088
11089 #if 1
11090   /* check if element under the player changes from accessible to unaccessible
11091      (needed for special case of dropping element which then changes) */
11092   /* (must be checked after creating new element for walkable group elements) */
11093 #if USE_FIX_KILLED_BY_NON_WALKABLE
11094   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11095       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11096   {
11097     Bang(x, y);
11098
11099     return;
11100   }
11101 #else
11102   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11103       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11104   {
11105     Bang(x, y);
11106
11107     return;
11108   }
11109 #endif
11110 #endif
11111
11112   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11113   if (new_element_is_player)
11114     RelocatePlayer(x, y, new_element);
11115
11116   if (is_change)
11117     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11118
11119   TestIfBadThingTouchesPlayer(x, y);
11120   TestIfPlayerTouchesCustomElement(x, y);
11121   TestIfElementTouchesCustomElement(x, y);
11122 }
11123
11124 static void CreateField(int x, int y, int element)
11125 {
11126   CreateFieldExt(x, y, element, FALSE);
11127 }
11128
11129 static void CreateElementFromChange(int x, int y, int element)
11130 {
11131   element = GET_VALID_RUNTIME_ELEMENT(element);
11132
11133 #if USE_STOP_CHANGED_ELEMENTS
11134   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11135   {
11136     int old_element = Feld[x][y];
11137
11138     /* prevent changed element from moving in same engine frame
11139        unless both old and new element can either fall or move */
11140     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11141         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11142       Stop[x][y] = TRUE;
11143   }
11144 #endif
11145
11146   CreateFieldExt(x, y, element, TRUE);
11147 }
11148
11149 static boolean ChangeElement(int x, int y, int element, int page)
11150 {
11151   struct ElementInfo *ei = &element_info[element];
11152   struct ElementChangeInfo *change = &ei->change_page[page];
11153   int ce_value = CustomValue[x][y];
11154   int ce_score = ei->collect_score;
11155   int target_element;
11156   int old_element = Feld[x][y];
11157
11158   /* always use default change event to prevent running into a loop */
11159   if (ChangeEvent[x][y] == -1)
11160     ChangeEvent[x][y] = CE_DELAY;
11161
11162   if (ChangeEvent[x][y] == CE_DELAY)
11163   {
11164     /* reset actual trigger element, trigger player and action element */
11165     change->actual_trigger_element = EL_EMPTY;
11166     change->actual_trigger_player = EL_EMPTY;
11167     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11168     change->actual_trigger_side = CH_SIDE_NONE;
11169     change->actual_trigger_ce_value = 0;
11170     change->actual_trigger_ce_score = 0;
11171   }
11172
11173   /* do not change elements more than a specified maximum number of changes */
11174   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11175     return FALSE;
11176
11177   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11178
11179   if (change->explode)
11180   {
11181     Bang(x, y);
11182
11183     return TRUE;
11184   }
11185
11186   if (change->use_target_content)
11187   {
11188     boolean complete_replace = TRUE;
11189     boolean can_replace[3][3];
11190     int xx, yy;
11191
11192     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11193     {
11194       boolean is_empty;
11195       boolean is_walkable;
11196       boolean is_diggable;
11197       boolean is_collectible;
11198       boolean is_removable;
11199       boolean is_destructible;
11200       int ex = x + xx - 1;
11201       int ey = y + yy - 1;
11202       int content_element = change->target_content.e[xx][yy];
11203       int e;
11204
11205       can_replace[xx][yy] = TRUE;
11206
11207       if (ex == x && ey == y)   /* do not check changing element itself */
11208         continue;
11209
11210       if (content_element == EL_EMPTY_SPACE)
11211       {
11212         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11213
11214         continue;
11215       }
11216
11217       if (!IN_LEV_FIELD(ex, ey))
11218       {
11219         can_replace[xx][yy] = FALSE;
11220         complete_replace = FALSE;
11221
11222         continue;
11223       }
11224
11225       e = Feld[ex][ey];
11226
11227       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11228         e = MovingOrBlocked2Element(ex, ey);
11229
11230       is_empty = (IS_FREE(ex, ey) ||
11231                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11232
11233       is_walkable     = (is_empty || IS_WALKABLE(e));
11234       is_diggable     = (is_empty || IS_DIGGABLE(e));
11235       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11236       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11237       is_removable    = (is_diggable || is_collectible);
11238
11239       can_replace[xx][yy] =
11240         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11241           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11242           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11243           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11244           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11245           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11246          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11247
11248       if (!can_replace[xx][yy])
11249         complete_replace = FALSE;
11250     }
11251
11252     if (!change->only_if_complete || complete_replace)
11253     {
11254       boolean something_has_changed = FALSE;
11255
11256       if (change->only_if_complete && change->use_random_replace &&
11257           RND(100) < change->random_percentage)
11258         return FALSE;
11259
11260       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11261       {
11262         int ex = x + xx - 1;
11263         int ey = y + yy - 1;
11264         int content_element;
11265
11266         if (can_replace[xx][yy] && (!change->use_random_replace ||
11267                                     RND(100) < change->random_percentage))
11268         {
11269           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11270             RemoveMovingField(ex, ey);
11271
11272           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11273
11274           content_element = change->target_content.e[xx][yy];
11275           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11276                                               ce_value, ce_score);
11277
11278           CreateElementFromChange(ex, ey, target_element);
11279
11280           something_has_changed = TRUE;
11281
11282           /* for symmetry reasons, freeze newly created border elements */
11283           if (ex != x || ey != y)
11284             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11285         }
11286       }
11287
11288       if (something_has_changed)
11289       {
11290         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11291         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11292       }
11293     }
11294   }
11295   else
11296   {
11297     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11298                                         ce_value, ce_score);
11299
11300     if (element == EL_DIAGONAL_GROWING ||
11301         element == EL_DIAGONAL_SHRINKING)
11302     {
11303       target_element = Store[x][y];
11304
11305       Store[x][y] = EL_EMPTY;
11306     }
11307
11308     CreateElementFromChange(x, y, target_element);
11309
11310     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11311     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11312   }
11313
11314   /* this uses direct change before indirect change */
11315   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11316
11317   return TRUE;
11318 }
11319
11320 #if USE_NEW_DELAYED_ACTION
11321
11322 static void HandleElementChange(int x, int y, int page)
11323 {
11324   int element = MovingOrBlocked2Element(x, y);
11325   struct ElementInfo *ei = &element_info[element];
11326   struct ElementChangeInfo *change = &ei->change_page[page];
11327   boolean handle_action_before_change = FALSE;
11328
11329 #ifdef DEBUG
11330   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11331       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11332   {
11333     printf("\n\n");
11334     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11335            x, y, element, element_info[element].token_name);
11336     printf("HandleElementChange(): This should never happen!\n");
11337     printf("\n\n");
11338   }
11339 #endif
11340
11341   /* this can happen with classic bombs on walkable, changing elements */
11342   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11343   {
11344 #if 0
11345     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11346       ChangeDelay[x][y] = 0;
11347 #endif
11348
11349     return;
11350   }
11351
11352   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11353   {
11354     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11355
11356     if (change->can_change)
11357     {
11358 #if 1
11359       /* !!! not clear why graphic animation should be reset at all here !!! */
11360       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11361 #if USE_GFX_RESET_WHEN_NOT_MOVING
11362       /* when a custom element is about to change (for example by change delay),
11363          do not reset graphic animation when the custom element is moving */
11364       if (!IS_MOVING(x, y))
11365 #endif
11366       {
11367         ResetGfxAnimation(x, y);
11368         ResetRandomAnimationValue(x, y);
11369       }
11370 #endif
11371
11372       if (change->pre_change_function)
11373         change->pre_change_function(x, y);
11374     }
11375   }
11376
11377   ChangeDelay[x][y]--;
11378
11379   if (ChangeDelay[x][y] != 0)           /* continue element change */
11380   {
11381     if (change->can_change)
11382     {
11383       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11384
11385       if (IS_ANIMATED(graphic))
11386         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11387
11388       if (change->change_function)
11389         change->change_function(x, y);
11390     }
11391   }
11392   else                                  /* finish element change */
11393   {
11394     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11395     {
11396       page = ChangePage[x][y];
11397       ChangePage[x][y] = -1;
11398
11399       change = &ei->change_page[page];
11400     }
11401
11402     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11403     {
11404       ChangeDelay[x][y] = 1;            /* try change after next move step */
11405       ChangePage[x][y] = page;          /* remember page to use for change */
11406
11407       return;
11408     }
11409
11410 #if 1
11411     /* special case: set new level random seed before changing element */
11412     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11413       handle_action_before_change = TRUE;
11414
11415     if (change->has_action && handle_action_before_change)
11416       ExecuteCustomElementAction(x, y, element, page);
11417 #endif
11418
11419     if (change->can_change)
11420     {
11421       if (ChangeElement(x, y, element, page))
11422       {
11423         if (change->post_change_function)
11424           change->post_change_function(x, y);
11425       }
11426     }
11427
11428     if (change->has_action && !handle_action_before_change)
11429       ExecuteCustomElementAction(x, y, element, page);
11430   }
11431 }
11432
11433 #else
11434
11435 static void HandleElementChange(int x, int y, int page)
11436 {
11437   int element = MovingOrBlocked2Element(x, y);
11438   struct ElementInfo *ei = &element_info[element];
11439   struct ElementChangeInfo *change = &ei->change_page[page];
11440
11441 #ifdef DEBUG
11442   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11443   {
11444     printf("\n\n");
11445     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11446            x, y, element, element_info[element].token_name);
11447     printf("HandleElementChange(): This should never happen!\n");
11448     printf("\n\n");
11449   }
11450 #endif
11451
11452   /* this can happen with classic bombs on walkable, changing elements */
11453   if (!CAN_CHANGE(element))
11454   {
11455 #if 0
11456     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11457       ChangeDelay[x][y] = 0;
11458 #endif
11459
11460     return;
11461   }
11462
11463   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11464   {
11465     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11466
11467     ResetGfxAnimation(x, y);
11468     ResetRandomAnimationValue(x, y);
11469
11470     if (change->pre_change_function)
11471       change->pre_change_function(x, y);
11472   }
11473
11474   ChangeDelay[x][y]--;
11475
11476   if (ChangeDelay[x][y] != 0)           /* continue element change */
11477   {
11478     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11479
11480     if (IS_ANIMATED(graphic))
11481       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11482
11483     if (change->change_function)
11484       change->change_function(x, y);
11485   }
11486   else                                  /* finish element change */
11487   {
11488     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11489     {
11490       page = ChangePage[x][y];
11491       ChangePage[x][y] = -1;
11492
11493       change = &ei->change_page[page];
11494     }
11495
11496     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11497     {
11498       ChangeDelay[x][y] = 1;            /* try change after next move step */
11499       ChangePage[x][y] = page;          /* remember page to use for change */
11500
11501       return;
11502     }
11503
11504     if (ChangeElement(x, y, element, page))
11505     {
11506       if (change->post_change_function)
11507         change->post_change_function(x, y);
11508     }
11509   }
11510 }
11511
11512 #endif
11513
11514 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11515                                               int trigger_element,
11516                                               int trigger_event,
11517                                               int trigger_player,
11518                                               int trigger_side,
11519                                               int trigger_page)
11520 {
11521   boolean change_done_any = FALSE;
11522   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11523   int i;
11524
11525   if (!(trigger_events[trigger_element][trigger_event]))
11526     return FALSE;
11527
11528 #if 0
11529   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11530          trigger_event, recursion_loop_depth, recursion_loop_detected,
11531          recursion_loop_element, EL_NAME(recursion_loop_element));
11532 #endif
11533
11534   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11535
11536   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11537   {
11538     int element = EL_CUSTOM_START + i;
11539     boolean change_done = FALSE;
11540     int p;
11541
11542     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11543         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11544       continue;
11545
11546     for (p = 0; p < element_info[element].num_change_pages; p++)
11547     {
11548       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11549
11550       if (change->can_change_or_has_action &&
11551           change->has_event[trigger_event] &&
11552           change->trigger_side & trigger_side &&
11553           change->trigger_player & trigger_player &&
11554           change->trigger_page & trigger_page_bits &&
11555           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11556       {
11557         change->actual_trigger_element = trigger_element;
11558         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11559         change->actual_trigger_player_bits = trigger_player;
11560         change->actual_trigger_side = trigger_side;
11561         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11562         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11563
11564 #if 0
11565         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11566                element, EL_NAME(element), p);
11567 #endif
11568
11569         if ((change->can_change && !change_done) || change->has_action)
11570         {
11571           int x, y;
11572
11573           SCAN_PLAYFIELD(x, y)
11574           {
11575             if (Feld[x][y] == element)
11576             {
11577               if (change->can_change && !change_done)
11578               {
11579 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11580                 /* if element already changed in this frame, not only prevent
11581                    another element change (checked in ChangeElement()), but
11582                    also prevent additional element actions for this element */
11583
11584                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11585                     !level.use_action_after_change_bug)
11586                   continue;
11587 #endif
11588
11589 #if 0
11590                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11591                        element, EL_NAME(element), p);
11592 #endif
11593
11594                 ChangeDelay[x][y] = 1;
11595                 ChangeEvent[x][y] = trigger_event;
11596
11597                 HandleElementChange(x, y, p);
11598               }
11599 #if USE_NEW_DELAYED_ACTION
11600               else if (change->has_action)
11601               {
11602 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11603                 /* if element already changed in this frame, not only prevent
11604                    another element change (checked in ChangeElement()), but
11605                    also prevent additional element actions for this element */
11606
11607                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11608                     !level.use_action_after_change_bug)
11609                   continue;
11610 #endif
11611
11612
11613 #if 0
11614                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11615                        element, EL_NAME(element), p);
11616 #endif
11617
11618                 ExecuteCustomElementAction(x, y, element, p);
11619                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11620               }
11621 #else
11622               if (change->has_action)
11623               {
11624                 ExecuteCustomElementAction(x, y, element, p);
11625                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11626               }
11627 #endif
11628             }
11629           }
11630
11631           if (change->can_change)
11632           {
11633             change_done = TRUE;
11634             change_done_any = TRUE;
11635
11636 #if 0
11637             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11638                    element, EL_NAME(element), p);
11639 #endif
11640
11641           }
11642         }
11643       }
11644     }
11645   }
11646
11647   RECURSION_LOOP_DETECTION_END();
11648
11649   return change_done_any;
11650 }
11651
11652 static boolean CheckElementChangeExt(int x, int y,
11653                                      int element,
11654                                      int trigger_element,
11655                                      int trigger_event,
11656                                      int trigger_player,
11657                                      int trigger_side)
11658 {
11659   boolean change_done = FALSE;
11660   int p;
11661
11662   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11663       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11664     return FALSE;
11665
11666   if (Feld[x][y] == EL_BLOCKED)
11667   {
11668     Blocked2Moving(x, y, &x, &y);
11669     element = Feld[x][y];
11670   }
11671
11672 #if 0
11673   /* check if element has already changed */
11674   if (Feld[x][y] != element)
11675     return FALSE;
11676 #else
11677   /* check if element has already changed or is about to change after moving */
11678   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11679        Feld[x][y] != element) ||
11680
11681       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11682        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11683         ChangePage[x][y] != -1)))
11684     return FALSE;
11685 #endif
11686
11687 #if 0
11688   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11689          trigger_event, recursion_loop_depth, recursion_loop_detected,
11690          recursion_loop_element, EL_NAME(recursion_loop_element));
11691 #endif
11692
11693   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11694
11695 #if 0
11696   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11697 #endif
11698
11699   for (p = 0; p < element_info[element].num_change_pages; p++)
11700   {
11701     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11702
11703     /* check trigger element for all events where the element that is checked
11704        for changing interacts with a directly adjacent element -- this is
11705        different to element changes that affect other elements to change on the
11706        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11707     boolean check_trigger_element =
11708       (trigger_event == CE_TOUCHING_X ||
11709        trigger_event == CE_HITTING_X ||
11710        trigger_event == CE_HIT_BY_X ||
11711 #if 1
11712        /* this one was forgotten until 3.2.3 */
11713        trigger_event == CE_DIGGING_X);
11714 #endif
11715
11716     if (change->can_change_or_has_action &&
11717         change->has_event[trigger_event] &&
11718         change->trigger_side & trigger_side &&
11719         change->trigger_player & trigger_player &&
11720         (!check_trigger_element ||
11721          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11722     {
11723       change->actual_trigger_element = trigger_element;
11724       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11725       change->actual_trigger_player_bits = trigger_player;
11726       change->actual_trigger_side = trigger_side;
11727       change->actual_trigger_ce_value = CustomValue[x][y];
11728       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11729
11730       /* special case: trigger element not at (x,y) position for some events */
11731       if (check_trigger_element)
11732       {
11733         static struct
11734         {
11735           int dx, dy;
11736         } move_xy[] =
11737           {
11738             {  0,  0 },
11739             { -1,  0 },
11740             { +1,  0 },
11741             {  0,  0 },
11742             {  0, -1 },
11743             {  0,  0 }, { 0, 0 }, { 0, 0 },
11744             {  0, +1 }
11745           };
11746
11747         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11748         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11749
11750         change->actual_trigger_ce_value = CustomValue[xx][yy];
11751         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11752       }
11753
11754       if (change->can_change && !change_done)
11755       {
11756         ChangeDelay[x][y] = 1;
11757         ChangeEvent[x][y] = trigger_event;
11758
11759         HandleElementChange(x, y, p);
11760
11761         change_done = TRUE;
11762       }
11763 #if USE_NEW_DELAYED_ACTION
11764       else if (change->has_action)
11765       {
11766         ExecuteCustomElementAction(x, y, element, p);
11767         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11768       }
11769 #else
11770       if (change->has_action)
11771       {
11772         ExecuteCustomElementAction(x, y, element, p);
11773         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11774       }
11775 #endif
11776     }
11777   }
11778
11779   RECURSION_LOOP_DETECTION_END();
11780
11781   return change_done;
11782 }
11783
11784 static void PlayPlayerSound(struct PlayerInfo *player)
11785 {
11786   int jx = player->jx, jy = player->jy;
11787   int sound_element = player->artwork_element;
11788   int last_action = player->last_action_waiting;
11789   int action = player->action_waiting;
11790
11791   if (player->is_waiting)
11792   {
11793     if (action != last_action)
11794       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11795     else
11796       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11797   }
11798   else
11799   {
11800     if (action != last_action)
11801       StopSound(element_info[sound_element].sound[last_action]);
11802
11803     if (last_action == ACTION_SLEEPING)
11804       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11805   }
11806 }
11807
11808 static void PlayAllPlayersSound()
11809 {
11810   int i;
11811
11812   for (i = 0; i < MAX_PLAYERS; i++)
11813     if (stored_player[i].active)
11814       PlayPlayerSound(&stored_player[i]);
11815 }
11816
11817 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11818 {
11819   boolean last_waiting = player->is_waiting;
11820   int move_dir = player->MovDir;
11821
11822   player->dir_waiting = move_dir;
11823   player->last_action_waiting = player->action_waiting;
11824
11825   if (is_waiting)
11826   {
11827     if (!last_waiting)          /* not waiting -> waiting */
11828     {
11829       player->is_waiting = TRUE;
11830
11831       player->frame_counter_bored =
11832         FrameCounter +
11833         game.player_boring_delay_fixed +
11834         GetSimpleRandom(game.player_boring_delay_random);
11835       player->frame_counter_sleeping =
11836         FrameCounter +
11837         game.player_sleeping_delay_fixed +
11838         GetSimpleRandom(game.player_sleeping_delay_random);
11839
11840       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11841     }
11842
11843     if (game.player_sleeping_delay_fixed +
11844         game.player_sleeping_delay_random > 0 &&
11845         player->anim_delay_counter == 0 &&
11846         player->post_delay_counter == 0 &&
11847         FrameCounter >= player->frame_counter_sleeping)
11848       player->is_sleeping = TRUE;
11849     else if (game.player_boring_delay_fixed +
11850              game.player_boring_delay_random > 0 &&
11851              FrameCounter >= player->frame_counter_bored)
11852       player->is_bored = TRUE;
11853
11854     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11855                               player->is_bored ? ACTION_BORING :
11856                               ACTION_WAITING);
11857
11858     if (player->is_sleeping && player->use_murphy)
11859     {
11860       /* special case for sleeping Murphy when leaning against non-free tile */
11861
11862       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11863           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11864            !IS_MOVING(player->jx - 1, player->jy)))
11865         move_dir = MV_LEFT;
11866       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11867                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11868                 !IS_MOVING(player->jx + 1, player->jy)))
11869         move_dir = MV_RIGHT;
11870       else
11871         player->is_sleeping = FALSE;
11872
11873       player->dir_waiting = move_dir;
11874     }
11875
11876     if (player->is_sleeping)
11877     {
11878       if (player->num_special_action_sleeping > 0)
11879       {
11880         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11881         {
11882           int last_special_action = player->special_action_sleeping;
11883           int num_special_action = player->num_special_action_sleeping;
11884           int special_action =
11885             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11886              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11887              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11888              last_special_action + 1 : ACTION_SLEEPING);
11889           int special_graphic =
11890             el_act_dir2img(player->artwork_element, special_action, move_dir);
11891
11892           player->anim_delay_counter =
11893             graphic_info[special_graphic].anim_delay_fixed +
11894             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11895           player->post_delay_counter =
11896             graphic_info[special_graphic].post_delay_fixed +
11897             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11898
11899           player->special_action_sleeping = special_action;
11900         }
11901
11902         if (player->anim_delay_counter > 0)
11903         {
11904           player->action_waiting = player->special_action_sleeping;
11905           player->anim_delay_counter--;
11906         }
11907         else if (player->post_delay_counter > 0)
11908         {
11909           player->post_delay_counter--;
11910         }
11911       }
11912     }
11913     else if (player->is_bored)
11914     {
11915       if (player->num_special_action_bored > 0)
11916       {
11917         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11918         {
11919           int special_action =
11920             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11921           int special_graphic =
11922             el_act_dir2img(player->artwork_element, special_action, move_dir);
11923
11924           player->anim_delay_counter =
11925             graphic_info[special_graphic].anim_delay_fixed +
11926             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11927           player->post_delay_counter =
11928             graphic_info[special_graphic].post_delay_fixed +
11929             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11930
11931           player->special_action_bored = special_action;
11932         }
11933
11934         if (player->anim_delay_counter > 0)
11935         {
11936           player->action_waiting = player->special_action_bored;
11937           player->anim_delay_counter--;
11938         }
11939         else if (player->post_delay_counter > 0)
11940         {
11941           player->post_delay_counter--;
11942         }
11943       }
11944     }
11945   }
11946   else if (last_waiting)        /* waiting -> not waiting */
11947   {
11948     player->is_waiting = FALSE;
11949     player->is_bored = FALSE;
11950     player->is_sleeping = FALSE;
11951
11952     player->frame_counter_bored = -1;
11953     player->frame_counter_sleeping = -1;
11954
11955     player->anim_delay_counter = 0;
11956     player->post_delay_counter = 0;
11957
11958     player->dir_waiting = player->MovDir;
11959     player->action_waiting = ACTION_DEFAULT;
11960
11961     player->special_action_bored = ACTION_DEFAULT;
11962     player->special_action_sleeping = ACTION_DEFAULT;
11963   }
11964 }
11965
11966 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11967 {
11968   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11969   int left      = player_action & JOY_LEFT;
11970   int right     = player_action & JOY_RIGHT;
11971   int up        = player_action & JOY_UP;
11972   int down      = player_action & JOY_DOWN;
11973   int button1   = player_action & JOY_BUTTON_1;
11974   int button2   = player_action & JOY_BUTTON_2;
11975   int dx        = (left ? -1 : right ? 1 : 0);
11976   int dy        = (up   ? -1 : down  ? 1 : 0);
11977
11978   if (!player->active || tape.pausing)
11979     return 0;
11980
11981   if (player_action)
11982   {
11983     if (button1)
11984       snapped = SnapField(player, dx, dy);
11985     else
11986     {
11987       if (button2)
11988         dropped = DropElement(player);
11989
11990       moved = MovePlayer(player, dx, dy);
11991     }
11992
11993     if (tape.single_step && tape.recording && !tape.pausing)
11994     {
11995 #if 1
11996       /* as it is called "single step mode", just return to pause mode when the
11997          player stopped moving after one tile (or never starts moving at all) */
11998       if (!player->is_moving)
11999 #else
12000       /* this is buggy: there are quite some cases where the single step mode
12001          does not return to pause mode (like pushing things that don't move
12002          or simply by trying to run against a wall) */
12003       if (button1 || (dropped && !moved))
12004 #endif
12005       {
12006         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12007         SnapField(player, 0, 0);                /* stop snapping */
12008       }
12009     }
12010
12011     SetPlayerWaiting(player, FALSE);
12012
12013     return player_action;
12014   }
12015   else
12016   {
12017     /* no actions for this player (no input at player's configured device) */
12018
12019     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12020     SnapField(player, 0, 0);
12021     CheckGravityMovementWhenNotMoving(player);
12022
12023     if (player->MovPos == 0)
12024       SetPlayerWaiting(player, TRUE);
12025
12026     if (player->MovPos == 0)    /* needed for tape.playing */
12027       player->is_moving = FALSE;
12028
12029     player->is_dropping = FALSE;
12030     player->is_dropping_pressed = FALSE;
12031     player->drop_pressed_delay = 0;
12032
12033     return 0;
12034   }
12035 }
12036
12037 static void CheckLevelTime()
12038 {
12039   int i;
12040
12041   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12042   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12043   {
12044     if (level.native_em_level->lev->home == 0)  /* all players at home */
12045     {
12046       PlayerWins(local_player);
12047
12048       AllPlayersGone = TRUE;
12049
12050       level.native_em_level->lev->home = -1;
12051     }
12052
12053     if (level.native_em_level->ply[0]->alive == 0 &&
12054         level.native_em_level->ply[1]->alive == 0 &&
12055         level.native_em_level->ply[2]->alive == 0 &&
12056         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12057       AllPlayersGone = TRUE;
12058   }
12059   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12060   {
12061     if (game_sp.LevelSolved &&
12062         !game_sp.GameOver)                              /* game won */
12063     {
12064       PlayerWins(local_player);
12065
12066       game_sp.GameOver = TRUE;
12067
12068       AllPlayersGone = TRUE;
12069     }
12070
12071     if (game_sp.GameOver)                               /* game lost */
12072       AllPlayersGone = TRUE;
12073   }
12074
12075   if (TimeFrames >= FRAMES_PER_SECOND)
12076   {
12077     TimeFrames = 0;
12078     TapeTime++;
12079
12080     for (i = 0; i < MAX_PLAYERS; i++)
12081     {
12082       struct PlayerInfo *player = &stored_player[i];
12083
12084       if (SHIELD_ON(player))
12085       {
12086         player->shield_normal_time_left--;
12087
12088         if (player->shield_deadly_time_left > 0)
12089           player->shield_deadly_time_left--;
12090       }
12091     }
12092
12093     if (!local_player->LevelSolved && !level.use_step_counter)
12094     {
12095       TimePlayed++;
12096
12097       if (TimeLeft > 0)
12098       {
12099         TimeLeft--;
12100
12101         if (TimeLeft <= 10 && setup.time_limit)
12102           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12103
12104 #if 1
12105         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12106
12107         DisplayGameControlValues();
12108 #else
12109         DrawGameValue_Time(TimeLeft);
12110 #endif
12111
12112         if (!TimeLeft && setup.time_limit)
12113         {
12114           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12115             level.native_em_level->lev->killed_out_of_time = TRUE;
12116           else
12117             for (i = 0; i < MAX_PLAYERS; i++)
12118               KillPlayer(&stored_player[i]);
12119         }
12120       }
12121 #if 1
12122       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12123       {
12124         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12125
12126         DisplayGameControlValues();
12127       }
12128 #else
12129       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12130         DrawGameValue_Time(TimePlayed);
12131 #endif
12132
12133       level.native_em_level->lev->time =
12134         (level.time == 0 ? TimePlayed : TimeLeft);
12135     }
12136
12137     if (tape.recording || tape.playing)
12138       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12139   }
12140
12141 #if 1
12142   UpdateAndDisplayGameControlValues();
12143 #else
12144   UpdateGameDoorValues();
12145   DrawGameDoorValues();
12146 #endif
12147 }
12148
12149 void AdvanceFrameAndPlayerCounters(int player_nr)
12150 {
12151   int i;
12152
12153   /* advance frame counters (global frame counter and time frame counter) */
12154   FrameCounter++;
12155   TimeFrames++;
12156
12157   /* advance player counters (counters for move delay, move animation etc.) */
12158   for (i = 0; i < MAX_PLAYERS; i++)
12159   {
12160     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12161     int move_delay_value = stored_player[i].move_delay_value;
12162     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12163
12164     if (!advance_player_counters)       /* not all players may be affected */
12165       continue;
12166
12167 #if USE_NEW_PLAYER_ANIM
12168     if (move_frames == 0)       /* less than one move per game frame */
12169     {
12170       int stepsize = TILEX / move_delay_value;
12171       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12172       int count = (stored_player[i].is_moving ?
12173                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12174
12175       if (count % delay == 0)
12176         move_frames = 1;
12177     }
12178 #endif
12179
12180     stored_player[i].Frame += move_frames;
12181
12182     if (stored_player[i].MovPos != 0)
12183       stored_player[i].StepFrame += move_frames;
12184
12185     if (stored_player[i].move_delay > 0)
12186       stored_player[i].move_delay--;
12187
12188     /* due to bugs in previous versions, counter must count up, not down */
12189     if (stored_player[i].push_delay != -1)
12190       stored_player[i].push_delay++;
12191
12192     if (stored_player[i].drop_delay > 0)
12193       stored_player[i].drop_delay--;
12194
12195     if (stored_player[i].is_dropping_pressed)
12196       stored_player[i].drop_pressed_delay++;
12197   }
12198 }
12199
12200 void StartGameActions(boolean init_network_game, boolean record_tape,
12201                       long random_seed)
12202 {
12203   unsigned long new_random_seed = InitRND(random_seed);
12204
12205   if (record_tape)
12206     TapeStartRecording(new_random_seed);
12207
12208 #if defined(NETWORK_AVALIABLE)
12209   if (init_network_game)
12210   {
12211     SendToServer_StartPlaying();
12212
12213     return;
12214   }
12215 #endif
12216
12217   InitGame();
12218 }
12219
12220 void GameActions()
12221 {
12222   static unsigned long game_frame_delay = 0;
12223   unsigned long game_frame_delay_value;
12224   byte *recorded_player_action;
12225   byte summarized_player_action = 0;
12226   byte tape_action[MAX_PLAYERS];
12227   int i;
12228
12229   /* detect endless loops, caused by custom element programming */
12230   if (recursion_loop_detected && recursion_loop_depth == 0)
12231   {
12232     char *message = getStringCat3("Internal Error ! Element ",
12233                                   EL_NAME(recursion_loop_element),
12234                                   " caused endless loop ! Quit the game ?");
12235
12236     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12237           EL_NAME(recursion_loop_element));
12238
12239     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12240
12241     recursion_loop_detected = FALSE;    /* if game should be continued */
12242
12243     free(message);
12244
12245     return;
12246   }
12247
12248   if (game.restart_level)
12249     StartGameActions(options.network, setup.autorecord, level.random_seed);
12250
12251   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12252   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12253   {
12254     if (level.native_em_level->lev->home == 0)  /* all players at home */
12255     {
12256       PlayerWins(local_player);
12257
12258       AllPlayersGone = TRUE;
12259
12260       level.native_em_level->lev->home = -1;
12261     }
12262
12263     if (level.native_em_level->ply[0]->alive == 0 &&
12264         level.native_em_level->ply[1]->alive == 0 &&
12265         level.native_em_level->ply[2]->alive == 0 &&
12266         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12267       AllPlayersGone = TRUE;
12268   }
12269   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12270   {
12271     if (game_sp.LevelSolved &&
12272         !game_sp.GameOver)                              /* game won */
12273     {
12274       PlayerWins(local_player);
12275
12276       game_sp.GameOver = TRUE;
12277
12278       AllPlayersGone = TRUE;
12279     }
12280
12281     if (game_sp.GameOver)                               /* game lost */
12282       AllPlayersGone = TRUE;
12283   }
12284
12285   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12286     GameWon();
12287
12288   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12289     TapeStop();
12290
12291   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12292     return;
12293
12294   game_frame_delay_value =
12295     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12296
12297   if (tape.playing && tape.warp_forward && !tape.pausing)
12298     game_frame_delay_value = 0;
12299
12300   /* ---------- main game synchronization point ---------- */
12301
12302   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12303
12304   if (network_playing && !network_player_action_received)
12305   {
12306     /* try to get network player actions in time */
12307
12308 #if defined(NETWORK_AVALIABLE)
12309     /* last chance to get network player actions without main loop delay */
12310     HandleNetworking();
12311 #endif
12312
12313     /* game was quit by network peer */
12314     if (game_status != GAME_MODE_PLAYING)
12315       return;
12316
12317     if (!network_player_action_received)
12318       return;           /* failed to get network player actions in time */
12319
12320     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12321   }
12322
12323   if (tape.pausing)
12324     return;
12325
12326   /* at this point we know that we really continue executing the game */
12327
12328   network_player_action_received = FALSE;
12329
12330   /* when playing tape, read previously recorded player input from tape data */
12331   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12332
12333 #if 1
12334   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12335   if (tape.pausing)
12336     return;
12337 #endif
12338
12339   if (tape.set_centered_player)
12340   {
12341     game.centered_player_nr_next = tape.centered_player_nr_next;
12342     game.set_centered_player = TRUE;
12343   }
12344
12345   for (i = 0; i < MAX_PLAYERS; i++)
12346   {
12347     summarized_player_action |= stored_player[i].action;
12348
12349     if (!network_playing)
12350       stored_player[i].effective_action = stored_player[i].action;
12351   }
12352
12353 #if defined(NETWORK_AVALIABLE)
12354   if (network_playing)
12355     SendToServer_MovePlayer(summarized_player_action);
12356 #endif
12357
12358   if (!options.network && !setup.team_mode)
12359     local_player->effective_action = summarized_player_action;
12360
12361   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12362   {
12363     for (i = 0; i < MAX_PLAYERS; i++)
12364       stored_player[i].effective_action =
12365         (i == game.centered_player_nr ? summarized_player_action : 0);
12366   }
12367
12368   if (recorded_player_action != NULL)
12369     for (i = 0; i < MAX_PLAYERS; i++)
12370       stored_player[i].effective_action = recorded_player_action[i];
12371
12372   for (i = 0; i < MAX_PLAYERS; i++)
12373   {
12374     tape_action[i] = stored_player[i].effective_action;
12375
12376     /* (this can only happen in the R'n'D game engine) */
12377     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12378       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12379   }
12380
12381   /* only record actions from input devices, but not programmed actions */
12382   if (tape.recording)
12383     TapeRecordAction(tape_action);
12384
12385 #if USE_NEW_PLAYER_ASSIGNMENTS
12386   {
12387     byte mapped_action[MAX_PLAYERS];
12388
12389     for (i = 0; i < MAX_PLAYERS; i++)
12390       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12391
12392     for (i = 0; i < MAX_PLAYERS; i++)
12393       stored_player[i].effective_action = mapped_action[i];
12394   }
12395 #endif
12396
12397   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12398   {
12399     GameActions_EM_Main();
12400   }
12401   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12402   {
12403     GameActions_SP_Main();
12404   }
12405   else
12406   {
12407     GameActions_RND();
12408   }
12409 }
12410
12411 void GameActions_EM_Main()
12412 {
12413   byte effective_action[MAX_PLAYERS];
12414   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12415   int i;
12416
12417   for (i = 0; i < MAX_PLAYERS; i++)
12418     effective_action[i] = stored_player[i].effective_action;
12419
12420   GameActions_EM(effective_action, warp_mode);
12421
12422   CheckLevelTime();
12423
12424   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12425 }
12426
12427 void GameActions_SP_Main()
12428 {
12429   byte effective_action[MAX_PLAYERS];
12430   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12431   int i;
12432
12433   for (i = 0; i < MAX_PLAYERS; i++)
12434     effective_action[i] = stored_player[i].effective_action;
12435
12436   GameActions_SP(effective_action, warp_mode);
12437
12438   CheckLevelTime();
12439
12440   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12441 }
12442
12443 void GameActions_RND()
12444 {
12445   int magic_wall_x = 0, magic_wall_y = 0;
12446   int i, x, y, element, graphic;
12447
12448   InitPlayfieldScanModeVars();
12449
12450 #if USE_ONE_MORE_CHANGE_PER_FRAME
12451   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12452   {
12453     SCAN_PLAYFIELD(x, y)
12454     {
12455       ChangeCount[x][y] = 0;
12456       ChangeEvent[x][y] = -1;
12457     }
12458   }
12459 #endif
12460
12461   if (game.set_centered_player)
12462   {
12463     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12464
12465     /* switching to "all players" only possible if all players fit to screen */
12466     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12467     {
12468       game.centered_player_nr_next = game.centered_player_nr;
12469       game.set_centered_player = FALSE;
12470     }
12471
12472     /* do not switch focus to non-existing (or non-active) player */
12473     if (game.centered_player_nr_next >= 0 &&
12474         !stored_player[game.centered_player_nr_next].active)
12475     {
12476       game.centered_player_nr_next = game.centered_player_nr;
12477       game.set_centered_player = FALSE;
12478     }
12479   }
12480
12481   if (game.set_centered_player &&
12482       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12483   {
12484     int sx, sy;
12485
12486     if (game.centered_player_nr_next == -1)
12487     {
12488       setScreenCenteredToAllPlayers(&sx, &sy);
12489     }
12490     else
12491     {
12492       sx = stored_player[game.centered_player_nr_next].jx;
12493       sy = stored_player[game.centered_player_nr_next].jy;
12494     }
12495
12496     game.centered_player_nr = game.centered_player_nr_next;
12497     game.set_centered_player = FALSE;
12498
12499     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12500     DrawGameDoorValues();
12501   }
12502
12503   for (i = 0; i < MAX_PLAYERS; i++)
12504   {
12505     int actual_player_action = stored_player[i].effective_action;
12506
12507 #if 1
12508     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12509        - rnd_equinox_tetrachloride 048
12510        - rnd_equinox_tetrachloride_ii 096
12511        - rnd_emanuel_schmieg 002
12512        - doctor_sloan_ww 001, 020
12513     */
12514     if (stored_player[i].MovPos == 0)
12515       CheckGravityMovement(&stored_player[i]);
12516 #endif
12517
12518     /* overwrite programmed action with tape action */
12519     if (stored_player[i].programmed_action)
12520       actual_player_action = stored_player[i].programmed_action;
12521
12522     PlayerActions(&stored_player[i], actual_player_action);
12523
12524     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12525   }
12526
12527   ScrollScreen(NULL, SCROLL_GO_ON);
12528
12529   /* for backwards compatibility, the following code emulates a fixed bug that
12530      occured when pushing elements (causing elements that just made their last
12531      pushing step to already (if possible) make their first falling step in the
12532      same game frame, which is bad); this code is also needed to use the famous
12533      "spring push bug" which is used in older levels and might be wanted to be
12534      used also in newer levels, but in this case the buggy pushing code is only
12535      affecting the "spring" element and no other elements */
12536
12537   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12538   {
12539     for (i = 0; i < MAX_PLAYERS; i++)
12540     {
12541       struct PlayerInfo *player = &stored_player[i];
12542       int x = player->jx;
12543       int y = player->jy;
12544
12545       if (player->active && player->is_pushing && player->is_moving &&
12546           IS_MOVING(x, y) &&
12547           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12548            Feld[x][y] == EL_SPRING))
12549       {
12550         ContinueMoving(x, y);
12551
12552         /* continue moving after pushing (this is actually a bug) */
12553         if (!IS_MOVING(x, y))
12554           Stop[x][y] = FALSE;
12555       }
12556     }
12557   }
12558
12559 #if 0
12560   debug_print_timestamp(0, "start main loop profiling");
12561 #endif
12562
12563   SCAN_PLAYFIELD(x, y)
12564   {
12565     ChangeCount[x][y] = 0;
12566     ChangeEvent[x][y] = -1;
12567
12568     /* this must be handled before main playfield loop */
12569     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12570     {
12571       MovDelay[x][y]--;
12572       if (MovDelay[x][y] <= 0)
12573         RemoveField(x, y);
12574     }
12575
12576 #if USE_NEW_SNAP_DELAY
12577     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12578     {
12579       MovDelay[x][y]--;
12580       if (MovDelay[x][y] <= 0)
12581       {
12582         RemoveField(x, y);
12583         TEST_DrawLevelField(x, y);
12584
12585         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12586       }
12587     }
12588 #endif
12589
12590 #if DEBUG
12591     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12592     {
12593       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12594       printf("GameActions(): This should never happen!\n");
12595
12596       ChangePage[x][y] = -1;
12597     }
12598 #endif
12599
12600     Stop[x][y] = FALSE;
12601     if (WasJustMoving[x][y] > 0)
12602       WasJustMoving[x][y]--;
12603     if (WasJustFalling[x][y] > 0)
12604       WasJustFalling[x][y]--;
12605     if (CheckCollision[x][y] > 0)
12606       CheckCollision[x][y]--;
12607     if (CheckImpact[x][y] > 0)
12608       CheckImpact[x][y]--;
12609
12610     GfxFrame[x][y]++;
12611
12612     /* reset finished pushing action (not done in ContinueMoving() to allow
12613        continuous pushing animation for elements with zero push delay) */
12614     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12615     {
12616       ResetGfxAnimation(x, y);
12617       TEST_DrawLevelField(x, y);
12618     }
12619
12620 #if DEBUG
12621     if (IS_BLOCKED(x, y))
12622     {
12623       int oldx, oldy;
12624
12625       Blocked2Moving(x, y, &oldx, &oldy);
12626       if (!IS_MOVING(oldx, oldy))
12627       {
12628         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12629         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12630         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12631         printf("GameActions(): This should never happen!\n");
12632       }
12633     }
12634 #endif
12635   }
12636
12637 #if 0
12638   debug_print_timestamp(0, "- time for pre-main loop:");
12639 #endif
12640
12641 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12642   SCAN_PLAYFIELD(x, y)
12643   {
12644     element = Feld[x][y];
12645     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12646
12647 #if 1
12648     {
12649 #if 1
12650       int element2 = element;
12651       int graphic2 = graphic;
12652 #else
12653       int element2 = Feld[x][y];
12654       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12655 #endif
12656       int last_gfx_frame = GfxFrame[x][y];
12657
12658       if (graphic_info[graphic2].anim_global_sync)
12659         GfxFrame[x][y] = FrameCounter;
12660       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12661         GfxFrame[x][y] = CustomValue[x][y];
12662       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12663         GfxFrame[x][y] = element_info[element2].collect_score;
12664       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12665         GfxFrame[x][y] = ChangeDelay[x][y];
12666
12667       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12668         DrawLevelGraphicAnimation(x, y, graphic2);
12669     }
12670 #else
12671     ResetGfxFrame(x, y, TRUE);
12672 #endif
12673
12674 #if 1
12675     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12676         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12677       ResetRandomAnimationValue(x, y);
12678 #endif
12679
12680 #if 1
12681     SetRandomAnimationValue(x, y);
12682 #endif
12683
12684 #if 1
12685     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12686 #endif
12687   }
12688 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12689
12690 #if 0
12691   debug_print_timestamp(0, "- time for TEST loop:     -->");
12692 #endif
12693
12694   SCAN_PLAYFIELD(x, y)
12695   {
12696     element = Feld[x][y];
12697     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12698
12699     ResetGfxFrame(x, y, TRUE);
12700
12701     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12702         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12703       ResetRandomAnimationValue(x, y);
12704
12705     SetRandomAnimationValue(x, y);
12706
12707     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12708
12709     if (IS_INACTIVE(element))
12710     {
12711       if (IS_ANIMATED(graphic))
12712         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12713
12714       continue;
12715     }
12716
12717     /* this may take place after moving, so 'element' may have changed */
12718     if (IS_CHANGING(x, y) &&
12719         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12720     {
12721       int page = element_info[element].event_page_nr[CE_DELAY];
12722
12723 #if 1
12724       HandleElementChange(x, y, page);
12725 #else
12726       if (CAN_CHANGE(element))
12727         HandleElementChange(x, y, page);
12728
12729       if (HAS_ACTION(element))
12730         ExecuteCustomElementAction(x, y, element, page);
12731 #endif
12732
12733       element = Feld[x][y];
12734       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12735     }
12736
12737 #if 0   // ---------------------------------------------------------------------
12738
12739     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12740     {
12741       StartMoving(x, y);
12742
12743       element = Feld[x][y];
12744       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12745
12746       if (IS_ANIMATED(graphic) &&
12747           !IS_MOVING(x, y) &&
12748           !Stop[x][y])
12749         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12750
12751       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12752         TEST_DrawTwinkleOnField(x, y);
12753     }
12754     else if (IS_MOVING(x, y))
12755       ContinueMoving(x, y);
12756     else
12757     {
12758       switch (element)
12759       {
12760         case EL_ACID:
12761         case EL_EXIT_OPEN:
12762         case EL_EM_EXIT_OPEN:
12763         case EL_SP_EXIT_OPEN:
12764         case EL_STEEL_EXIT_OPEN:
12765         case EL_EM_STEEL_EXIT_OPEN:
12766         case EL_SP_TERMINAL:
12767         case EL_SP_TERMINAL_ACTIVE:
12768         case EL_EXTRA_TIME:
12769         case EL_SHIELD_NORMAL:
12770         case EL_SHIELD_DEADLY:
12771           if (IS_ANIMATED(graphic))
12772             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12773           break;
12774
12775         case EL_DYNAMITE_ACTIVE:
12776         case EL_EM_DYNAMITE_ACTIVE:
12777         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12778         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12779         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12780         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12781         case EL_SP_DISK_RED_ACTIVE:
12782           CheckDynamite(x, y);
12783           break;
12784
12785         case EL_AMOEBA_GROWING:
12786           AmoebeWaechst(x, y);
12787           break;
12788
12789         case EL_AMOEBA_SHRINKING:
12790           AmoebaDisappearing(x, y);
12791           break;
12792
12793 #if !USE_NEW_AMOEBA_CODE
12794         case EL_AMOEBA_WET:
12795         case EL_AMOEBA_DRY:
12796         case EL_AMOEBA_FULL:
12797         case EL_BD_AMOEBA:
12798         case EL_EMC_DRIPPER:
12799           AmoebeAbleger(x, y);
12800           break;
12801 #endif
12802
12803         case EL_GAME_OF_LIFE:
12804         case EL_BIOMAZE:
12805           Life(x, y);
12806           break;
12807
12808         case EL_EXIT_CLOSED:
12809           CheckExit(x, y);
12810           break;
12811
12812         case EL_EM_EXIT_CLOSED:
12813           CheckExitEM(x, y);
12814           break;
12815
12816         case EL_STEEL_EXIT_CLOSED:
12817           CheckExitSteel(x, y);
12818           break;
12819
12820         case EL_EM_STEEL_EXIT_CLOSED:
12821           CheckExitSteelEM(x, y);
12822           break;
12823
12824         case EL_SP_EXIT_CLOSED:
12825           CheckExitSP(x, y);
12826           break;
12827
12828         case EL_EXPANDABLE_WALL_GROWING:
12829         case EL_EXPANDABLE_STEELWALL_GROWING:
12830           MauerWaechst(x, y);
12831           break;
12832
12833         case EL_EXPANDABLE_WALL:
12834         case EL_EXPANDABLE_WALL_HORIZONTAL:
12835         case EL_EXPANDABLE_WALL_VERTICAL:
12836         case EL_EXPANDABLE_WALL_ANY:
12837         case EL_BD_EXPANDABLE_WALL:
12838           MauerAbleger(x, y);
12839           break;
12840
12841         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12842         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12843         case EL_EXPANDABLE_STEELWALL_ANY:
12844           MauerAblegerStahl(x, y);
12845           break;
12846
12847         case EL_FLAMES:
12848           CheckForDragon(x, y);
12849           break;
12850
12851         case EL_EXPLOSION:
12852           break;
12853
12854         case EL_ELEMENT_SNAPPING:
12855         case EL_DIAGONAL_SHRINKING:
12856         case EL_DIAGONAL_GROWING:
12857         {
12858           graphic =
12859             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12860
12861           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12862           break;
12863         }
12864
12865         default:
12866           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12867             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12868           break;
12869       }
12870     }
12871
12872 #else   // ---------------------------------------------------------------------
12873
12874     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12875     {
12876       StartMoving(x, y);
12877
12878       element = Feld[x][y];
12879       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12880
12881       if (IS_ANIMATED(graphic) &&
12882           !IS_MOVING(x, y) &&
12883           !Stop[x][y])
12884         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12885
12886       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12887         TEST_DrawTwinkleOnField(x, y);
12888     }
12889     else if ((element == EL_ACID ||
12890               element == EL_EXIT_OPEN ||
12891               element == EL_EM_EXIT_OPEN ||
12892               element == EL_SP_EXIT_OPEN ||
12893               element == EL_STEEL_EXIT_OPEN ||
12894               element == EL_EM_STEEL_EXIT_OPEN ||
12895               element == EL_SP_TERMINAL ||
12896               element == EL_SP_TERMINAL_ACTIVE ||
12897               element == EL_EXTRA_TIME ||
12898               element == EL_SHIELD_NORMAL ||
12899               element == EL_SHIELD_DEADLY) &&
12900              IS_ANIMATED(graphic))
12901       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12902     else if (IS_MOVING(x, y))
12903       ContinueMoving(x, y);
12904     else if (IS_ACTIVE_BOMB(element))
12905       CheckDynamite(x, y);
12906     else if (element == EL_AMOEBA_GROWING)
12907       AmoebeWaechst(x, y);
12908     else if (element == EL_AMOEBA_SHRINKING)
12909       AmoebaDisappearing(x, y);
12910
12911 #if !USE_NEW_AMOEBA_CODE
12912     else if (IS_AMOEBALIVE(element))
12913       AmoebeAbleger(x, y);
12914 #endif
12915
12916     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12917       Life(x, y);
12918     else if (element == EL_EXIT_CLOSED)
12919       CheckExit(x, y);
12920     else if (element == EL_EM_EXIT_CLOSED)
12921       CheckExitEM(x, y);
12922     else if (element == EL_STEEL_EXIT_CLOSED)
12923       CheckExitSteel(x, y);
12924     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12925       CheckExitSteelEM(x, y);
12926     else if (element == EL_SP_EXIT_CLOSED)
12927       CheckExitSP(x, y);
12928     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12929              element == EL_EXPANDABLE_STEELWALL_GROWING)
12930       MauerWaechst(x, y);
12931     else if (element == EL_EXPANDABLE_WALL ||
12932              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12933              element == EL_EXPANDABLE_WALL_VERTICAL ||
12934              element == EL_EXPANDABLE_WALL_ANY ||
12935              element == EL_BD_EXPANDABLE_WALL)
12936       MauerAbleger(x, y);
12937     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12938              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12939              element == EL_EXPANDABLE_STEELWALL_ANY)
12940       MauerAblegerStahl(x, y);
12941     else if (element == EL_FLAMES)
12942       CheckForDragon(x, y);
12943     else if (element == EL_EXPLOSION)
12944       ; /* drawing of correct explosion animation is handled separately */
12945     else if (element == EL_ELEMENT_SNAPPING ||
12946              element == EL_DIAGONAL_SHRINKING ||
12947              element == EL_DIAGONAL_GROWING)
12948     {
12949       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12950
12951       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12952     }
12953     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12954       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12955
12956 #endif  // ---------------------------------------------------------------------
12957
12958     if (IS_BELT_ACTIVE(element))
12959       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12960
12961     if (game.magic_wall_active)
12962     {
12963       int jx = local_player->jx, jy = local_player->jy;
12964
12965       /* play the element sound at the position nearest to the player */
12966       if ((element == EL_MAGIC_WALL_FULL ||
12967            element == EL_MAGIC_WALL_ACTIVE ||
12968            element == EL_MAGIC_WALL_EMPTYING ||
12969            element == EL_BD_MAGIC_WALL_FULL ||
12970            element == EL_BD_MAGIC_WALL_ACTIVE ||
12971            element == EL_BD_MAGIC_WALL_EMPTYING ||
12972            element == EL_DC_MAGIC_WALL_FULL ||
12973            element == EL_DC_MAGIC_WALL_ACTIVE ||
12974            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12975           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12976       {
12977         magic_wall_x = x;
12978         magic_wall_y = y;
12979       }
12980     }
12981   }
12982
12983 #if 0
12984   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12985 #endif
12986
12987 #if USE_NEW_AMOEBA_CODE
12988   /* new experimental amoeba growth stuff */
12989   if (!(FrameCounter % 8))
12990   {
12991     static unsigned long random = 1684108901;
12992
12993     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12994     {
12995       x = RND(lev_fieldx);
12996       y = RND(lev_fieldy);
12997       element = Feld[x][y];
12998
12999       if (!IS_PLAYER(x,y) &&
13000           (element == EL_EMPTY ||
13001            CAN_GROW_INTO(element) ||
13002            element == EL_QUICKSAND_EMPTY ||
13003            element == EL_QUICKSAND_FAST_EMPTY ||
13004            element == EL_ACID_SPLASH_LEFT ||
13005            element == EL_ACID_SPLASH_RIGHT))
13006       {
13007         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13008             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13009             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13010             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13011           Feld[x][y] = EL_AMOEBA_DROP;
13012       }
13013
13014       random = random * 129 + 1;
13015     }
13016   }
13017 #endif
13018
13019 #if 0
13020   if (game.explosions_delayed)
13021 #endif
13022   {
13023     game.explosions_delayed = FALSE;
13024
13025     SCAN_PLAYFIELD(x, y)
13026     {
13027       element = Feld[x][y];
13028
13029       if (ExplodeField[x][y])
13030         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13031       else if (element == EL_EXPLOSION)
13032         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13033
13034       ExplodeField[x][y] = EX_TYPE_NONE;
13035     }
13036
13037     game.explosions_delayed = TRUE;
13038   }
13039
13040   if (game.magic_wall_active)
13041   {
13042     if (!(game.magic_wall_time_left % 4))
13043     {
13044       int element = Feld[magic_wall_x][magic_wall_y];
13045
13046       if (element == EL_BD_MAGIC_WALL_FULL ||
13047           element == EL_BD_MAGIC_WALL_ACTIVE ||
13048           element == EL_BD_MAGIC_WALL_EMPTYING)
13049         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13050       else if (element == EL_DC_MAGIC_WALL_FULL ||
13051                element == EL_DC_MAGIC_WALL_ACTIVE ||
13052                element == EL_DC_MAGIC_WALL_EMPTYING)
13053         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13054       else
13055         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13056     }
13057
13058     if (game.magic_wall_time_left > 0)
13059     {
13060       game.magic_wall_time_left--;
13061
13062       if (!game.magic_wall_time_left)
13063       {
13064         SCAN_PLAYFIELD(x, y)
13065         {
13066           element = Feld[x][y];
13067
13068           if (element == EL_MAGIC_WALL_ACTIVE ||
13069               element == EL_MAGIC_WALL_FULL)
13070           {
13071             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13072             TEST_DrawLevelField(x, y);
13073           }
13074           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13075                    element == EL_BD_MAGIC_WALL_FULL)
13076           {
13077             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13078             TEST_DrawLevelField(x, y);
13079           }
13080           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13081                    element == EL_DC_MAGIC_WALL_FULL)
13082           {
13083             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13084             TEST_DrawLevelField(x, y);
13085           }
13086         }
13087
13088         game.magic_wall_active = FALSE;
13089       }
13090     }
13091   }
13092
13093   if (game.light_time_left > 0)
13094   {
13095     game.light_time_left--;
13096
13097     if (game.light_time_left == 0)
13098       RedrawAllLightSwitchesAndInvisibleElements();
13099   }
13100
13101   if (game.timegate_time_left > 0)
13102   {
13103     game.timegate_time_left--;
13104
13105     if (game.timegate_time_left == 0)
13106       CloseAllOpenTimegates();
13107   }
13108
13109   if (game.lenses_time_left > 0)
13110   {
13111     game.lenses_time_left--;
13112
13113     if (game.lenses_time_left == 0)
13114       RedrawAllInvisibleElementsForLenses();
13115   }
13116
13117   if (game.magnify_time_left > 0)
13118   {
13119     game.magnify_time_left--;
13120
13121     if (game.magnify_time_left == 0)
13122       RedrawAllInvisibleElementsForMagnifier();
13123   }
13124
13125   for (i = 0; i < MAX_PLAYERS; i++)
13126   {
13127     struct PlayerInfo *player = &stored_player[i];
13128
13129     if (SHIELD_ON(player))
13130     {
13131       if (player->shield_deadly_time_left)
13132         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13133       else if (player->shield_normal_time_left)
13134         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13135     }
13136   }
13137
13138 #if USE_DELAYED_GFX_REDRAW
13139   SCAN_PLAYFIELD(x, y)
13140   {
13141 #if 1
13142     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13143 #else
13144     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13145         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13146 #endif
13147     {
13148       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13149          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13150
13151       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13152         DrawLevelField(x, y);
13153
13154       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13155         DrawLevelFieldCrumbledSand(x, y);
13156
13157       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13158         DrawLevelFieldCrumbledSandNeighbours(x, y);
13159
13160       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13161         DrawTwinkleOnField(x, y);
13162     }
13163
13164     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13165   }
13166 #endif
13167
13168   CheckLevelTime();
13169
13170   DrawAllPlayers();
13171   PlayAllPlayersSound();
13172
13173   if (options.debug)                    /* calculate frames per second */
13174   {
13175     static unsigned long fps_counter = 0;
13176     static int fps_frames = 0;
13177     unsigned long fps_delay_ms = Counter() - fps_counter;
13178
13179     fps_frames++;
13180
13181     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13182     {
13183       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13184
13185       fps_frames = 0;
13186       fps_counter = Counter();
13187     }
13188
13189     redraw_mask |= REDRAW_FPS;
13190   }
13191
13192   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13193
13194   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13195   {
13196     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13197
13198     local_player->show_envelope = 0;
13199   }
13200
13201 #if 0
13202   debug_print_timestamp(0, "stop main loop profiling ");
13203   printf("----------------------------------------------------------\n");
13204 #endif
13205
13206   /* use random number generator in every frame to make it less predictable */
13207   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13208     RND(1);
13209 }
13210
13211 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13212 {
13213   int min_x = x, min_y = y, max_x = x, max_y = y;
13214   int i;
13215
13216   for (i = 0; i < MAX_PLAYERS; i++)
13217   {
13218     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13219
13220     if (!stored_player[i].active || &stored_player[i] == player)
13221       continue;
13222
13223     min_x = MIN(min_x, jx);
13224     min_y = MIN(min_y, jy);
13225     max_x = MAX(max_x, jx);
13226     max_y = MAX(max_y, jy);
13227   }
13228
13229   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13230 }
13231
13232 static boolean AllPlayersInVisibleScreen()
13233 {
13234   int i;
13235
13236   for (i = 0; i < MAX_PLAYERS; i++)
13237   {
13238     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13239
13240     if (!stored_player[i].active)
13241       continue;
13242
13243     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13244       return FALSE;
13245   }
13246
13247   return TRUE;
13248 }
13249
13250 void ScrollLevel(int dx, int dy)
13251 {
13252 #if 0
13253   /* (directly solved in BlitBitmap() now) */
13254   static Bitmap *bitmap_db_field2 = NULL;
13255   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13256   int x, y;
13257 #else
13258   int x, y;
13259 #endif
13260
13261 #if 0
13262   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13263   /* only horizontal XOR vertical scroll direction allowed */
13264   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13265     return;
13266 #endif
13267
13268 #if 0
13269   /* (directly solved in BlitBitmap() now) */
13270   if (bitmap_db_field2 == NULL)
13271     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13272
13273   /* needed when blitting directly to same bitmap -- should not be needed with
13274      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13275   BlitBitmap(drawto_field, bitmap_db_field2,
13276              FX + TILEX * (dx == -1) - softscroll_offset,
13277              FY + TILEY * (dy == -1) - softscroll_offset,
13278              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13279              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13280              FX + TILEX * (dx == 1) - softscroll_offset,
13281              FY + TILEY * (dy == 1) - softscroll_offset);
13282   BlitBitmap(bitmap_db_field2, drawto_field,
13283              FX + TILEX * (dx == 1) - softscroll_offset,
13284              FY + TILEY * (dy == 1) - softscroll_offset,
13285              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13286              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13287              FX + TILEX * (dx == 1) - softscroll_offset,
13288              FY + TILEY * (dy == 1) - softscroll_offset);
13289
13290 #else
13291
13292 #if 0
13293   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13294   int xsize = (BX2 - BX1 + 1);
13295   int ysize = (BY2 - BY1 + 1);
13296   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13297   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13298   int step  = (start < end ? +1 : -1);
13299
13300   for (i = start; i != end; i += step)
13301   {
13302     BlitBitmap(drawto_field, drawto_field,
13303                FX + TILEX * (dx != 0 ? i + step : 0),
13304                FY + TILEY * (dy != 0 ? i + step : 0),
13305                TILEX * (dx != 0 ? 1 : xsize),
13306                TILEY * (dy != 0 ? 1 : ysize),
13307                FX + TILEX * (dx != 0 ? i : 0),
13308                FY + TILEY * (dy != 0 ? i : 0));
13309   }
13310
13311 #else
13312
13313   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13314
13315   BlitBitmap(drawto_field, drawto_field,
13316              FX + TILEX * (dx == -1) - softscroll_offset,
13317              FY + TILEY * (dy == -1) - softscroll_offset,
13318              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13319              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13320              FX + TILEX * (dx == 1) - softscroll_offset,
13321              FY + TILEY * (dy == 1) - softscroll_offset);
13322 #endif
13323 #endif
13324
13325   if (dx != 0)
13326   {
13327     x = (dx == 1 ? BX1 : BX2);
13328     for (y = BY1; y <= BY2; y++)
13329       DrawScreenField(x, y);
13330   }
13331
13332   if (dy != 0)
13333   {
13334     y = (dy == 1 ? BY1 : BY2);
13335     for (x = BX1; x <= BX2; x++)
13336       DrawScreenField(x, y);
13337   }
13338
13339   redraw_mask |= REDRAW_FIELD;
13340 }
13341
13342 static boolean canFallDown(struct PlayerInfo *player)
13343 {
13344   int jx = player->jx, jy = player->jy;
13345
13346   return (IN_LEV_FIELD(jx, jy + 1) &&
13347           (IS_FREE(jx, jy + 1) ||
13348            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13349           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13350           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13351 }
13352
13353 static boolean canPassField(int x, int y, int move_dir)
13354 {
13355   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13356   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13357   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13358   int nextx = x + dx;
13359   int nexty = y + dy;
13360   int element = Feld[x][y];
13361
13362   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13363           !CAN_MOVE(element) &&
13364           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13365           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13366           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13367 }
13368
13369 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13370 {
13371   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13372   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13373   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13374   int newx = x + dx;
13375   int newy = y + dy;
13376
13377   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13378           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13379           (IS_DIGGABLE(Feld[newx][newy]) ||
13380            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13381            canPassField(newx, newy, move_dir)));
13382 }
13383
13384 static void CheckGravityMovement(struct PlayerInfo *player)
13385 {
13386 #if USE_PLAYER_GRAVITY
13387   if (player->gravity && !player->programmed_action)
13388 #else
13389   if (game.gravity && !player->programmed_action)
13390 #endif
13391   {
13392     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13393     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13394     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13395     int jx = player->jx, jy = player->jy;
13396     boolean player_is_moving_to_valid_field =
13397       (!player_is_snapping &&
13398        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13399         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13400     boolean player_can_fall_down = canFallDown(player);
13401
13402     if (player_can_fall_down &&
13403         !player_is_moving_to_valid_field)
13404       player->programmed_action = MV_DOWN;
13405   }
13406 }
13407
13408 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13409 {
13410   return CheckGravityMovement(player);
13411
13412 #if USE_PLAYER_GRAVITY
13413   if (player->gravity && !player->programmed_action)
13414 #else
13415   if (game.gravity && !player->programmed_action)
13416 #endif
13417   {
13418     int jx = player->jx, jy = player->jy;
13419     boolean field_under_player_is_free =
13420       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13421     boolean player_is_standing_on_valid_field =
13422       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13423        (IS_WALKABLE(Feld[jx][jy]) &&
13424         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13425
13426     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13427       player->programmed_action = MV_DOWN;
13428   }
13429 }
13430
13431 /*
13432   MovePlayerOneStep()
13433   -----------------------------------------------------------------------------
13434   dx, dy:               direction (non-diagonal) to try to move the player to
13435   real_dx, real_dy:     direction as read from input device (can be diagonal)
13436 */
13437
13438 boolean MovePlayerOneStep(struct PlayerInfo *player,
13439                           int dx, int dy, int real_dx, int real_dy)
13440 {
13441   int jx = player->jx, jy = player->jy;
13442   int new_jx = jx + dx, new_jy = jy + dy;
13443 #if !USE_FIXED_DONT_RUN_INTO
13444   int element;
13445 #endif
13446   int can_move;
13447   boolean player_can_move = !player->cannot_move;
13448
13449   if (!player->active || (!dx && !dy))
13450     return MP_NO_ACTION;
13451
13452   player->MovDir = (dx < 0 ? MV_LEFT :
13453                     dx > 0 ? MV_RIGHT :
13454                     dy < 0 ? MV_UP :
13455                     dy > 0 ? MV_DOWN :  MV_NONE);
13456
13457   if (!IN_LEV_FIELD(new_jx, new_jy))
13458     return MP_NO_ACTION;
13459
13460   if (!player_can_move)
13461   {
13462     if (player->MovPos == 0)
13463     {
13464       player->is_moving = FALSE;
13465       player->is_digging = FALSE;
13466       player->is_collecting = FALSE;
13467       player->is_snapping = FALSE;
13468       player->is_pushing = FALSE;
13469     }
13470   }
13471
13472 #if 1
13473   if (!options.network && game.centered_player_nr == -1 &&
13474       !AllPlayersInSight(player, new_jx, new_jy))
13475     return MP_NO_ACTION;
13476 #else
13477   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13478     return MP_NO_ACTION;
13479 #endif
13480
13481 #if !USE_FIXED_DONT_RUN_INTO
13482   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13483
13484   /* (moved to DigField()) */
13485   if (player_can_move && DONT_RUN_INTO(element))
13486   {
13487     if (element == EL_ACID && dx == 0 && dy == 1)
13488     {
13489       SplashAcid(new_jx, new_jy);
13490       Feld[jx][jy] = EL_PLAYER_1;
13491       InitMovingField(jx, jy, MV_DOWN);
13492       Store[jx][jy] = EL_ACID;
13493       ContinueMoving(jx, jy);
13494       BuryPlayer(player);
13495     }
13496     else
13497       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13498
13499     return MP_MOVING;
13500   }
13501 #endif
13502
13503   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13504   if (can_move != MP_MOVING)
13505     return can_move;
13506
13507   /* check if DigField() has caused relocation of the player */
13508   if (player->jx != jx || player->jy != jy)
13509     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13510
13511   StorePlayer[jx][jy] = 0;
13512   player->last_jx = jx;
13513   player->last_jy = jy;
13514   player->jx = new_jx;
13515   player->jy = new_jy;
13516   StorePlayer[new_jx][new_jy] = player->element_nr;
13517
13518   if (player->move_delay_value_next != -1)
13519   {
13520     player->move_delay_value = player->move_delay_value_next;
13521     player->move_delay_value_next = -1;
13522   }
13523
13524   player->MovPos =
13525     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13526
13527   player->step_counter++;
13528
13529   PlayerVisit[jx][jy] = FrameCounter;
13530
13531 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13532   player->is_moving = TRUE;
13533 #endif
13534
13535 #if 1
13536   /* should better be called in MovePlayer(), but this breaks some tapes */
13537   ScrollPlayer(player, SCROLL_INIT);
13538 #endif
13539
13540   return MP_MOVING;
13541 }
13542
13543 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13544 {
13545   int jx = player->jx, jy = player->jy;
13546   int old_jx = jx, old_jy = jy;
13547   int moved = MP_NO_ACTION;
13548
13549   if (!player->active)
13550     return FALSE;
13551
13552   if (!dx && !dy)
13553   {
13554     if (player->MovPos == 0)
13555     {
13556       player->is_moving = FALSE;
13557       player->is_digging = FALSE;
13558       player->is_collecting = FALSE;
13559       player->is_snapping = FALSE;
13560       player->is_pushing = FALSE;
13561     }
13562
13563     return FALSE;
13564   }
13565
13566   if (player->move_delay > 0)
13567     return FALSE;
13568
13569   player->move_delay = -1;              /* set to "uninitialized" value */
13570
13571   /* store if player is automatically moved to next field */
13572   player->is_auto_moving = (player->programmed_action != MV_NONE);
13573
13574   /* remove the last programmed player action */
13575   player->programmed_action = 0;
13576
13577   if (player->MovPos)
13578   {
13579     /* should only happen if pre-1.2 tape recordings are played */
13580     /* this is only for backward compatibility */
13581
13582     int original_move_delay_value = player->move_delay_value;
13583
13584 #if DEBUG
13585     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13586            tape.counter);
13587 #endif
13588
13589     /* scroll remaining steps with finest movement resolution */
13590     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13591
13592     while (player->MovPos)
13593     {
13594       ScrollPlayer(player, SCROLL_GO_ON);
13595       ScrollScreen(NULL, SCROLL_GO_ON);
13596
13597       AdvanceFrameAndPlayerCounters(player->index_nr);
13598
13599       DrawAllPlayers();
13600       BackToFront();
13601     }
13602
13603     player->move_delay_value = original_move_delay_value;
13604   }
13605
13606   player->is_active = FALSE;
13607
13608   if (player->last_move_dir & MV_HORIZONTAL)
13609   {
13610     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13611       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13612   }
13613   else
13614   {
13615     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13616       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13617   }
13618
13619 #if USE_FIXED_BORDER_RUNNING_GFX
13620   if (!moved && !player->is_active)
13621   {
13622     player->is_moving = FALSE;
13623     player->is_digging = FALSE;
13624     player->is_collecting = FALSE;
13625     player->is_snapping = FALSE;
13626     player->is_pushing = FALSE;
13627   }
13628 #endif
13629
13630   jx = player->jx;
13631   jy = player->jy;
13632
13633 #if 1
13634   if (moved & MP_MOVING && !ScreenMovPos &&
13635       (player->index_nr == game.centered_player_nr ||
13636        game.centered_player_nr == -1))
13637 #else
13638   if (moved & MP_MOVING && !ScreenMovPos &&
13639       (player == local_player || !options.network))
13640 #endif
13641   {
13642     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13643     int offset = game.scroll_delay_value;
13644
13645     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13646     {
13647       /* actual player has left the screen -- scroll in that direction */
13648       if (jx != old_jx)         /* player has moved horizontally */
13649         scroll_x += (jx - old_jx);
13650       else                      /* player has moved vertically */
13651         scroll_y += (jy - old_jy);
13652     }
13653     else
13654     {
13655       if (jx != old_jx)         /* player has moved horizontally */
13656       {
13657         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13658             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13659           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13660
13661         /* don't scroll over playfield boundaries */
13662         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13663           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13664
13665         /* don't scroll more than one field at a time */
13666         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13667
13668         /* don't scroll against the player's moving direction */
13669         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13670             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13671           scroll_x = old_scroll_x;
13672       }
13673       else                      /* player has moved vertically */
13674       {
13675         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13676             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13677           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13678
13679         /* don't scroll over playfield boundaries */
13680         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13681           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13682
13683         /* don't scroll more than one field at a time */
13684         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13685
13686         /* don't scroll against the player's moving direction */
13687         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13688             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13689           scroll_y = old_scroll_y;
13690       }
13691     }
13692
13693     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13694     {
13695 #if 1
13696       if (!options.network && game.centered_player_nr == -1 &&
13697           !AllPlayersInVisibleScreen())
13698       {
13699         scroll_x = old_scroll_x;
13700         scroll_y = old_scroll_y;
13701       }
13702       else
13703 #else
13704       if (!options.network && !AllPlayersInVisibleScreen())
13705       {
13706         scroll_x = old_scroll_x;
13707         scroll_y = old_scroll_y;
13708       }
13709       else
13710 #endif
13711       {
13712         ScrollScreen(player, SCROLL_INIT);
13713         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13714       }
13715     }
13716   }
13717
13718   player->StepFrame = 0;
13719
13720   if (moved & MP_MOVING)
13721   {
13722     if (old_jx != jx && old_jy == jy)
13723       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13724     else if (old_jx == jx && old_jy != jy)
13725       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13726
13727     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13728
13729     player->last_move_dir = player->MovDir;
13730     player->is_moving = TRUE;
13731     player->is_snapping = FALSE;
13732     player->is_switching = FALSE;
13733     player->is_dropping = FALSE;
13734     player->is_dropping_pressed = FALSE;
13735     player->drop_pressed_delay = 0;
13736
13737 #if 0
13738     /* should better be called here than above, but this breaks some tapes */
13739     ScrollPlayer(player, SCROLL_INIT);
13740 #endif
13741   }
13742   else
13743   {
13744     CheckGravityMovementWhenNotMoving(player);
13745
13746     player->is_moving = FALSE;
13747
13748     /* at this point, the player is allowed to move, but cannot move right now
13749        (e.g. because of something blocking the way) -- ensure that the player
13750        is also allowed to move in the next frame (in old versions before 3.1.1,
13751        the player was forced to wait again for eight frames before next try) */
13752
13753     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13754       player->move_delay = 0;   /* allow direct movement in the next frame */
13755   }
13756
13757   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13758     player->move_delay = player->move_delay_value;
13759
13760   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13761   {
13762     TestIfPlayerTouchesBadThing(jx, jy);
13763     TestIfPlayerTouchesCustomElement(jx, jy);
13764   }
13765
13766   if (!player->active)
13767     RemovePlayer(player);
13768
13769   return moved;
13770 }
13771
13772 void ScrollPlayer(struct PlayerInfo *player, int mode)
13773 {
13774   int jx = player->jx, jy = player->jy;
13775   int last_jx = player->last_jx, last_jy = player->last_jy;
13776   int move_stepsize = TILEX / player->move_delay_value;
13777
13778 #if USE_NEW_PLAYER_SPEED
13779   if (!player->active)
13780     return;
13781
13782   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13783     return;
13784 #else
13785   if (!player->active || player->MovPos == 0)
13786     return;
13787 #endif
13788
13789   if (mode == SCROLL_INIT)
13790   {
13791     player->actual_frame_counter = FrameCounter;
13792     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13793
13794     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13795         Feld[last_jx][last_jy] == EL_EMPTY)
13796     {
13797       int last_field_block_delay = 0;   /* start with no blocking at all */
13798       int block_delay_adjustment = player->block_delay_adjustment;
13799
13800       /* if player blocks last field, add delay for exactly one move */
13801       if (player->block_last_field)
13802       {
13803         last_field_block_delay += player->move_delay_value;
13804
13805         /* when blocking enabled, prevent moving up despite gravity */
13806 #if USE_PLAYER_GRAVITY
13807         if (player->gravity && player->MovDir == MV_UP)
13808           block_delay_adjustment = -1;
13809 #else
13810         if (game.gravity && player->MovDir == MV_UP)
13811           block_delay_adjustment = -1;
13812 #endif
13813       }
13814
13815       /* add block delay adjustment (also possible when not blocking) */
13816       last_field_block_delay += block_delay_adjustment;
13817
13818       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13819       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13820     }
13821
13822 #if USE_NEW_PLAYER_SPEED
13823     if (player->MovPos != 0)    /* player has not yet reached destination */
13824       return;
13825 #else
13826     return;
13827 #endif
13828   }
13829   else if (!FrameReached(&player->actual_frame_counter, 1))
13830     return;
13831
13832 #if USE_NEW_PLAYER_SPEED
13833   if (player->MovPos != 0)
13834   {
13835     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13836     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13837
13838     /* before DrawPlayer() to draw correct player graphic for this case */
13839     if (player->MovPos == 0)
13840       CheckGravityMovement(player);
13841   }
13842 #else
13843   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13844   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13845
13846   /* before DrawPlayer() to draw correct player graphic for this case */
13847   if (player->MovPos == 0)
13848     CheckGravityMovement(player);
13849 #endif
13850
13851   if (player->MovPos == 0)      /* player reached destination field */
13852   {
13853     if (player->move_delay_reset_counter > 0)
13854     {
13855       player->move_delay_reset_counter--;
13856
13857       if (player->move_delay_reset_counter == 0)
13858       {
13859         /* continue with normal speed after quickly moving through gate */
13860         HALVE_PLAYER_SPEED(player);
13861
13862         /* be able to make the next move without delay */
13863         player->move_delay = 0;
13864       }
13865     }
13866
13867     player->last_jx = jx;
13868     player->last_jy = jy;
13869
13870     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13871         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13872 #if 1
13873         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13874 #endif
13875         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13876         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13877 #if 1
13878         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13879 #endif
13880         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13881         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13882     {
13883       DrawPlayer(player);       /* needed here only to cleanup last field */
13884       RemovePlayer(player);
13885
13886       if (local_player->friends_still_needed == 0 ||
13887           IS_SP_ELEMENT(Feld[jx][jy]))
13888         PlayerWins(player);
13889     }
13890
13891     /* this breaks one level: "machine", level 000 */
13892     {
13893       int move_direction = player->MovDir;
13894       int enter_side = MV_DIR_OPPOSITE(move_direction);
13895       int leave_side = move_direction;
13896       int old_jx = last_jx;
13897       int old_jy = last_jy;
13898       int old_element = Feld[old_jx][old_jy];
13899       int new_element = Feld[jx][jy];
13900
13901       if (IS_CUSTOM_ELEMENT(old_element))
13902         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13903                                    CE_LEFT_BY_PLAYER,
13904                                    player->index_bit, leave_side);
13905
13906       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13907                                           CE_PLAYER_LEAVES_X,
13908                                           player->index_bit, leave_side);
13909
13910       if (IS_CUSTOM_ELEMENT(new_element))
13911         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13912                                    player->index_bit, enter_side);
13913
13914       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13915                                           CE_PLAYER_ENTERS_X,
13916                                           player->index_bit, enter_side);
13917
13918 #if USE_FIX_CE_ACTION_WITH_PLAYER
13919       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13920                                         CE_MOVE_OF_X, move_direction);
13921 #else
13922       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13923                                         CE_MOVE_OF_X, move_direction);
13924 #endif
13925     }
13926
13927     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13928     {
13929       TestIfPlayerTouchesBadThing(jx, jy);
13930       TestIfPlayerTouchesCustomElement(jx, jy);
13931
13932       /* needed because pushed element has not yet reached its destination,
13933          so it would trigger a change event at its previous field location */
13934       if (!player->is_pushing)
13935         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13936
13937       if (!player->active)
13938         RemovePlayer(player);
13939     }
13940
13941     if (!local_player->LevelSolved && level.use_step_counter)
13942     {
13943       int i;
13944
13945       TimePlayed++;
13946
13947       if (TimeLeft > 0)
13948       {
13949         TimeLeft--;
13950
13951         if (TimeLeft <= 10 && setup.time_limit)
13952           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13953
13954 #if 1
13955         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13956
13957         DisplayGameControlValues();
13958 #else
13959         DrawGameValue_Time(TimeLeft);
13960 #endif
13961
13962         if (!TimeLeft && setup.time_limit)
13963           for (i = 0; i < MAX_PLAYERS; i++)
13964             KillPlayer(&stored_player[i]);
13965       }
13966 #if 1
13967       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13968       {
13969         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13970
13971         DisplayGameControlValues();
13972       }
13973 #else
13974       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13975         DrawGameValue_Time(TimePlayed);
13976 #endif
13977     }
13978
13979     if (tape.single_step && tape.recording && !tape.pausing &&
13980         !player->programmed_action)
13981       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13982   }
13983 }
13984
13985 void ScrollScreen(struct PlayerInfo *player, int mode)
13986 {
13987   static unsigned long screen_frame_counter = 0;
13988
13989   if (mode == SCROLL_INIT)
13990   {
13991     /* set scrolling step size according to actual player's moving speed */
13992     ScrollStepSize = TILEX / player->move_delay_value;
13993
13994     screen_frame_counter = FrameCounter;
13995     ScreenMovDir = player->MovDir;
13996     ScreenMovPos = player->MovPos;
13997     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13998     return;
13999   }
14000   else if (!FrameReached(&screen_frame_counter, 1))
14001     return;
14002
14003   if (ScreenMovPos)
14004   {
14005     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14006     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14007     redraw_mask |= REDRAW_FIELD;
14008   }
14009   else
14010     ScreenMovDir = MV_NONE;
14011 }
14012
14013 void TestIfPlayerTouchesCustomElement(int x, int y)
14014 {
14015   static int xy[4][2] =
14016   {
14017     { 0, -1 },
14018     { -1, 0 },
14019     { +1, 0 },
14020     { 0, +1 }
14021   };
14022   static int trigger_sides[4][2] =
14023   {
14024     /* center side       border side */
14025     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14026     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14027     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14028     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14029   };
14030   static int touch_dir[4] =
14031   {
14032     MV_LEFT | MV_RIGHT,
14033     MV_UP   | MV_DOWN,
14034     MV_UP   | MV_DOWN,
14035     MV_LEFT | MV_RIGHT
14036   };
14037   int center_element = Feld[x][y];      /* should always be non-moving! */
14038   int i;
14039
14040   for (i = 0; i < NUM_DIRECTIONS; i++)
14041   {
14042     int xx = x + xy[i][0];
14043     int yy = y + xy[i][1];
14044     int center_side = trigger_sides[i][0];
14045     int border_side = trigger_sides[i][1];
14046     int border_element;
14047
14048     if (!IN_LEV_FIELD(xx, yy))
14049       continue;
14050
14051     if (IS_PLAYER(x, y))                /* player found at center element */
14052     {
14053       struct PlayerInfo *player = PLAYERINFO(x, y);
14054
14055       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14056         border_element = Feld[xx][yy];          /* may be moving! */
14057       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14058         border_element = Feld[xx][yy];
14059       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14060         border_element = MovingOrBlocked2Element(xx, yy);
14061       else
14062         continue;               /* center and border element do not touch */
14063
14064       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14065                                  player->index_bit, border_side);
14066       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14067                                           CE_PLAYER_TOUCHES_X,
14068                                           player->index_bit, border_side);
14069
14070 #if USE_FIX_CE_ACTION_WITH_PLAYER
14071       {
14072         /* use player element that is initially defined in the level playfield,
14073            not the player element that corresponds to the runtime player number
14074            (example: a level that contains EL_PLAYER_3 as the only player would
14075            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14076         int player_element = PLAYERINFO(x, y)->initial_element;
14077
14078         CheckElementChangeBySide(xx, yy, border_element, player_element,
14079                                  CE_TOUCHING_X, border_side);
14080       }
14081 #endif
14082     }
14083     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14084     {
14085       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14086
14087       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14088       {
14089         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14090           continue;             /* center and border element do not touch */
14091       }
14092
14093       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14094                                  player->index_bit, center_side);
14095       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14096                                           CE_PLAYER_TOUCHES_X,
14097                                           player->index_bit, center_side);
14098
14099 #if USE_FIX_CE_ACTION_WITH_PLAYER
14100       {
14101         /* use player element that is initially defined in the level playfield,
14102            not the player element that corresponds to the runtime player number
14103            (example: a level that contains EL_PLAYER_3 as the only player would
14104            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14105         int player_element = PLAYERINFO(xx, yy)->initial_element;
14106
14107         CheckElementChangeBySide(x, y, center_element, player_element,
14108                                  CE_TOUCHING_X, center_side);
14109       }
14110 #endif
14111
14112       break;
14113     }
14114   }
14115 }
14116
14117 #if USE_ELEMENT_TOUCHING_BUGFIX
14118
14119 void TestIfElementTouchesCustomElement(int x, int y)
14120 {
14121   static int xy[4][2] =
14122   {
14123     { 0, -1 },
14124     { -1, 0 },
14125     { +1, 0 },
14126     { 0, +1 }
14127   };
14128   static int trigger_sides[4][2] =
14129   {
14130     /* center side      border side */
14131     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14132     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14133     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14134     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14135   };
14136   static int touch_dir[4] =
14137   {
14138     MV_LEFT | MV_RIGHT,
14139     MV_UP   | MV_DOWN,
14140     MV_UP   | MV_DOWN,
14141     MV_LEFT | MV_RIGHT
14142   };
14143   boolean change_center_element = FALSE;
14144   int center_element = Feld[x][y];      /* should always be non-moving! */
14145   int border_element_old[NUM_DIRECTIONS];
14146   int i;
14147
14148   for (i = 0; i < NUM_DIRECTIONS; i++)
14149   {
14150     int xx = x + xy[i][0];
14151     int yy = y + xy[i][1];
14152     int border_element;
14153
14154     border_element_old[i] = -1;
14155
14156     if (!IN_LEV_FIELD(xx, yy))
14157       continue;
14158
14159     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14160       border_element = Feld[xx][yy];    /* may be moving! */
14161     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14162       border_element = Feld[xx][yy];
14163     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14164       border_element = MovingOrBlocked2Element(xx, yy);
14165     else
14166       continue;                 /* center and border element do not touch */
14167
14168     border_element_old[i] = border_element;
14169   }
14170
14171   for (i = 0; i < NUM_DIRECTIONS; i++)
14172   {
14173     int xx = x + xy[i][0];
14174     int yy = y + xy[i][1];
14175     int center_side = trigger_sides[i][0];
14176     int border_element = border_element_old[i];
14177
14178     if (border_element == -1)
14179       continue;
14180
14181     /* check for change of border element */
14182     CheckElementChangeBySide(xx, yy, border_element, center_element,
14183                              CE_TOUCHING_X, center_side);
14184
14185     /* (center element cannot be player, so we dont have to check this here) */
14186   }
14187
14188   for (i = 0; i < NUM_DIRECTIONS; i++)
14189   {
14190     int xx = x + xy[i][0];
14191     int yy = y + xy[i][1];
14192     int border_side = trigger_sides[i][1];
14193     int border_element = border_element_old[i];
14194
14195     if (border_element == -1)
14196       continue;
14197
14198     /* check for change of center element (but change it only once) */
14199     if (!change_center_element)
14200       change_center_element =
14201         CheckElementChangeBySide(x, y, center_element, border_element,
14202                                  CE_TOUCHING_X, border_side);
14203
14204 #if USE_FIX_CE_ACTION_WITH_PLAYER
14205     if (IS_PLAYER(xx, yy))
14206     {
14207       /* use player element that is initially defined in the level playfield,
14208          not the player element that corresponds to the runtime player number
14209          (example: a level that contains EL_PLAYER_3 as the only player would
14210          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14211       int player_element = PLAYERINFO(xx, yy)->initial_element;
14212
14213       CheckElementChangeBySide(x, y, center_element, player_element,
14214                                CE_TOUCHING_X, border_side);
14215     }
14216 #endif
14217   }
14218 }
14219
14220 #else
14221
14222 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14223 {
14224   static int xy[4][2] =
14225   {
14226     { 0, -1 },
14227     { -1, 0 },
14228     { +1, 0 },
14229     { 0, +1 }
14230   };
14231   static int trigger_sides[4][2] =
14232   {
14233     /* center side      border side */
14234     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14235     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14236     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14237     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14238   };
14239   static int touch_dir[4] =
14240   {
14241     MV_LEFT | MV_RIGHT,
14242     MV_UP   | MV_DOWN,
14243     MV_UP   | MV_DOWN,
14244     MV_LEFT | MV_RIGHT
14245   };
14246   boolean change_center_element = FALSE;
14247   int center_element = Feld[x][y];      /* should always be non-moving! */
14248   int i;
14249
14250   for (i = 0; i < NUM_DIRECTIONS; i++)
14251   {
14252     int xx = x + xy[i][0];
14253     int yy = y + xy[i][1];
14254     int center_side = trigger_sides[i][0];
14255     int border_side = trigger_sides[i][1];
14256     int border_element;
14257
14258     if (!IN_LEV_FIELD(xx, yy))
14259       continue;
14260
14261     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14262       border_element = Feld[xx][yy];    /* may be moving! */
14263     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14264       border_element = Feld[xx][yy];
14265     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14266       border_element = MovingOrBlocked2Element(xx, yy);
14267     else
14268       continue;                 /* center and border element do not touch */
14269
14270     /* check for change of center element (but change it only once) */
14271     if (!change_center_element)
14272       change_center_element =
14273         CheckElementChangeBySide(x, y, center_element, border_element,
14274                                  CE_TOUCHING_X, border_side);
14275
14276     /* check for change of border element */
14277     CheckElementChangeBySide(xx, yy, border_element, center_element,
14278                              CE_TOUCHING_X, center_side);
14279   }
14280 }
14281
14282 #endif
14283
14284 void TestIfElementHitsCustomElement(int x, int y, int direction)
14285 {
14286   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14287   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14288   int hitx = x + dx, hity = y + dy;
14289   int hitting_element = Feld[x][y];
14290   int touched_element;
14291
14292   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14293     return;
14294
14295   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14296                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14297
14298   if (IN_LEV_FIELD(hitx, hity))
14299   {
14300     int opposite_direction = MV_DIR_OPPOSITE(direction);
14301     int hitting_side = direction;
14302     int touched_side = opposite_direction;
14303     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14304                           MovDir[hitx][hity] != direction ||
14305                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14306
14307     object_hit = TRUE;
14308
14309     if (object_hit)
14310     {
14311       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14312                                CE_HITTING_X, touched_side);
14313
14314       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14315                                CE_HIT_BY_X, hitting_side);
14316
14317       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318                                CE_HIT_BY_SOMETHING, opposite_direction);
14319
14320 #if USE_FIX_CE_ACTION_WITH_PLAYER
14321       if (IS_PLAYER(hitx, hity))
14322       {
14323         /* use player element that is initially defined in the level playfield,
14324            not the player element that corresponds to the runtime player number
14325            (example: a level that contains EL_PLAYER_3 as the only player would
14326            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14327         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14328
14329         CheckElementChangeBySide(x, y, hitting_element, player_element,
14330                                  CE_HITTING_X, touched_side);
14331       }
14332 #endif
14333     }
14334   }
14335
14336   /* "hitting something" is also true when hitting the playfield border */
14337   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14338                            CE_HITTING_SOMETHING, direction);
14339 }
14340
14341 #if 0
14342 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14343 {
14344   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14345   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14346   int hitx = x + dx, hity = y + dy;
14347   int hitting_element = Feld[x][y];
14348   int touched_element;
14349 #if 0
14350   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14351                         !IS_FREE(hitx, hity) &&
14352                         (!IS_MOVING(hitx, hity) ||
14353                          MovDir[hitx][hity] != direction ||
14354                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14355 #endif
14356
14357   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14358     return;
14359
14360 #if 0
14361   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14362     return;
14363 #endif
14364
14365   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14366                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14367
14368   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14369                            EP_CAN_SMASH_EVERYTHING, direction);
14370
14371   if (IN_LEV_FIELD(hitx, hity))
14372   {
14373     int opposite_direction = MV_DIR_OPPOSITE(direction);
14374     int hitting_side = direction;
14375     int touched_side = opposite_direction;
14376 #if 0
14377     int touched_element = MovingOrBlocked2Element(hitx, hity);
14378 #endif
14379 #if 1
14380     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14381                           MovDir[hitx][hity] != direction ||
14382                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14383
14384     object_hit = TRUE;
14385 #endif
14386
14387     if (object_hit)
14388     {
14389       int i;
14390
14391       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14392                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14393
14394       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14395                                CE_OTHER_IS_SMASHING, touched_side);
14396
14397       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14398                                CE_OTHER_GETS_SMASHED, hitting_side);
14399     }
14400   }
14401 }
14402 #endif
14403
14404 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14405 {
14406   int i, kill_x = -1, kill_y = -1;
14407
14408   int bad_element = -1;
14409   static int test_xy[4][2] =
14410   {
14411     { 0, -1 },
14412     { -1, 0 },
14413     { +1, 0 },
14414     { 0, +1 }
14415   };
14416   static int test_dir[4] =
14417   {
14418     MV_UP,
14419     MV_LEFT,
14420     MV_RIGHT,
14421     MV_DOWN
14422   };
14423
14424   for (i = 0; i < NUM_DIRECTIONS; i++)
14425   {
14426     int test_x, test_y, test_move_dir, test_element;
14427
14428     test_x = good_x + test_xy[i][0];
14429     test_y = good_y + test_xy[i][1];
14430
14431     if (!IN_LEV_FIELD(test_x, test_y))
14432       continue;
14433
14434     test_move_dir =
14435       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14436
14437     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14438
14439     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14440        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14441     */
14442     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14443         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14444     {
14445       kill_x = test_x;
14446       kill_y = test_y;
14447       bad_element = test_element;
14448
14449       break;
14450     }
14451   }
14452
14453   if (kill_x != -1 || kill_y != -1)
14454   {
14455     if (IS_PLAYER(good_x, good_y))
14456     {
14457       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14458
14459       if (player->shield_deadly_time_left > 0 &&
14460           !IS_INDESTRUCTIBLE(bad_element))
14461         Bang(kill_x, kill_y);
14462       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14463         KillPlayer(player);
14464     }
14465     else
14466       Bang(good_x, good_y);
14467   }
14468 }
14469
14470 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14471 {
14472   int i, kill_x = -1, kill_y = -1;
14473   int bad_element = Feld[bad_x][bad_y];
14474   static int test_xy[4][2] =
14475   {
14476     { 0, -1 },
14477     { -1, 0 },
14478     { +1, 0 },
14479     { 0, +1 }
14480   };
14481   static int touch_dir[4] =
14482   {
14483     MV_LEFT | MV_RIGHT,
14484     MV_UP   | MV_DOWN,
14485     MV_UP   | MV_DOWN,
14486     MV_LEFT | MV_RIGHT
14487   };
14488   static int test_dir[4] =
14489   {
14490     MV_UP,
14491     MV_LEFT,
14492     MV_RIGHT,
14493     MV_DOWN
14494   };
14495
14496   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14497     return;
14498
14499   for (i = 0; i < NUM_DIRECTIONS; i++)
14500   {
14501     int test_x, test_y, test_move_dir, test_element;
14502
14503     test_x = bad_x + test_xy[i][0];
14504     test_y = bad_y + test_xy[i][1];
14505
14506     if (!IN_LEV_FIELD(test_x, test_y))
14507       continue;
14508
14509     test_move_dir =
14510       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14511
14512     test_element = Feld[test_x][test_y];
14513
14514     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14515        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14516     */
14517     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14518         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14519     {
14520       /* good thing is player or penguin that does not move away */
14521       if (IS_PLAYER(test_x, test_y))
14522       {
14523         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14524
14525         if (bad_element == EL_ROBOT && player->is_moving)
14526           continue;     /* robot does not kill player if he is moving */
14527
14528         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14529         {
14530           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14531             continue;           /* center and border element do not touch */
14532         }
14533
14534         kill_x = test_x;
14535         kill_y = test_y;
14536
14537         break;
14538       }
14539       else if (test_element == EL_PENGUIN)
14540       {
14541         kill_x = test_x;
14542         kill_y = test_y;
14543
14544         break;
14545       }
14546     }
14547   }
14548
14549   if (kill_x != -1 || kill_y != -1)
14550   {
14551     if (IS_PLAYER(kill_x, kill_y))
14552     {
14553       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14554
14555       if (player->shield_deadly_time_left > 0 &&
14556           !IS_INDESTRUCTIBLE(bad_element))
14557         Bang(bad_x, bad_y);
14558       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14559         KillPlayer(player);
14560     }
14561     else
14562       Bang(kill_x, kill_y);
14563   }
14564 }
14565
14566 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14567 {
14568   int bad_element = Feld[bad_x][bad_y];
14569   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14570   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14571   int test_x = bad_x + dx, test_y = bad_y + dy;
14572   int test_move_dir, test_element;
14573   int kill_x = -1, kill_y = -1;
14574
14575   if (!IN_LEV_FIELD(test_x, test_y))
14576     return;
14577
14578   test_move_dir =
14579     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14580
14581   test_element = Feld[test_x][test_y];
14582
14583   if (test_move_dir != bad_move_dir)
14584   {
14585     /* good thing can be player or penguin that does not move away */
14586     if (IS_PLAYER(test_x, test_y))
14587     {
14588       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14589
14590       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14591          player as being hit when he is moving towards the bad thing, because
14592          the "get hit by" condition would be lost after the player stops) */
14593       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14594         return;         /* player moves away from bad thing */
14595
14596       kill_x = test_x;
14597       kill_y = test_y;
14598     }
14599     else if (test_element == EL_PENGUIN)
14600     {
14601       kill_x = test_x;
14602       kill_y = test_y;
14603     }
14604   }
14605
14606   if (kill_x != -1 || kill_y != -1)
14607   {
14608     if (IS_PLAYER(kill_x, kill_y))
14609     {
14610       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14611
14612       if (player->shield_deadly_time_left > 0 &&
14613           !IS_INDESTRUCTIBLE(bad_element))
14614         Bang(bad_x, bad_y);
14615       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14616         KillPlayer(player);
14617     }
14618     else
14619       Bang(kill_x, kill_y);
14620   }
14621 }
14622
14623 void TestIfPlayerTouchesBadThing(int x, int y)
14624 {
14625   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14626 }
14627
14628 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14629 {
14630   TestIfGoodThingHitsBadThing(x, y, move_dir);
14631 }
14632
14633 void TestIfBadThingTouchesPlayer(int x, int y)
14634 {
14635   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14636 }
14637
14638 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14639 {
14640   TestIfBadThingHitsGoodThing(x, y, move_dir);
14641 }
14642
14643 void TestIfFriendTouchesBadThing(int x, int y)
14644 {
14645   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14646 }
14647
14648 void TestIfBadThingTouchesFriend(int x, int y)
14649 {
14650   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14651 }
14652
14653 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14654 {
14655   int i, kill_x = bad_x, kill_y = bad_y;
14656   static int xy[4][2] =
14657   {
14658     { 0, -1 },
14659     { -1, 0 },
14660     { +1, 0 },
14661     { 0, +1 }
14662   };
14663
14664   for (i = 0; i < NUM_DIRECTIONS; i++)
14665   {
14666     int x, y, element;
14667
14668     x = bad_x + xy[i][0];
14669     y = bad_y + xy[i][1];
14670     if (!IN_LEV_FIELD(x, y))
14671       continue;
14672
14673     element = Feld[x][y];
14674     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14675         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14676     {
14677       kill_x = x;
14678       kill_y = y;
14679       break;
14680     }
14681   }
14682
14683   if (kill_x != bad_x || kill_y != bad_y)
14684     Bang(bad_x, bad_y);
14685 }
14686
14687 void KillPlayer(struct PlayerInfo *player)
14688 {
14689   int jx = player->jx, jy = player->jy;
14690
14691   if (!player->active)
14692     return;
14693
14694 #if 0
14695   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14696          player->killed, player->active, player->reanimated);
14697 #endif
14698
14699   /* the following code was introduced to prevent an infinite loop when calling
14700      -> Bang()
14701      -> CheckTriggeredElementChangeExt()
14702      -> ExecuteCustomElementAction()
14703      -> KillPlayer()
14704      -> (infinitely repeating the above sequence of function calls)
14705      which occurs when killing the player while having a CE with the setting
14706      "kill player X when explosion of <player X>"; the solution using a new
14707      field "player->killed" was chosen for backwards compatibility, although
14708      clever use of the fields "player->active" etc. would probably also work */
14709 #if 1
14710   if (player->killed)
14711     return;
14712 #endif
14713
14714   player->killed = TRUE;
14715
14716   /* remove accessible field at the player's position */
14717   Feld[jx][jy] = EL_EMPTY;
14718
14719   /* deactivate shield (else Bang()/Explode() would not work right) */
14720   player->shield_normal_time_left = 0;
14721   player->shield_deadly_time_left = 0;
14722
14723 #if 0
14724   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14725          player->killed, player->active, player->reanimated);
14726 #endif
14727
14728   Bang(jx, jy);
14729
14730 #if 0
14731   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14732          player->killed, player->active, player->reanimated);
14733 #endif
14734
14735 #if USE_PLAYER_REANIMATION
14736 #if 1
14737   if (player->reanimated)       /* killed player may have been reanimated */
14738     player->killed = player->reanimated = FALSE;
14739   else
14740     BuryPlayer(player);
14741 #else
14742   if (player->killed)           /* player may have been reanimated */
14743     BuryPlayer(player);
14744 #endif
14745 #else
14746   BuryPlayer(player);
14747 #endif
14748 }
14749
14750 static void KillPlayerUnlessEnemyProtected(int x, int y)
14751 {
14752   if (!PLAYER_ENEMY_PROTECTED(x, y))
14753     KillPlayer(PLAYERINFO(x, y));
14754 }
14755
14756 static void KillPlayerUnlessExplosionProtected(int x, int y)
14757 {
14758   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14759     KillPlayer(PLAYERINFO(x, y));
14760 }
14761
14762 void BuryPlayer(struct PlayerInfo *player)
14763 {
14764   int jx = player->jx, jy = player->jy;
14765
14766   if (!player->active)
14767     return;
14768
14769   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14770   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14771
14772   player->GameOver = TRUE;
14773   RemovePlayer(player);
14774 }
14775
14776 void RemovePlayer(struct PlayerInfo *player)
14777 {
14778   int jx = player->jx, jy = player->jy;
14779   int i, found = FALSE;
14780
14781   player->present = FALSE;
14782   player->active = FALSE;
14783
14784   if (!ExplodeField[jx][jy])
14785     StorePlayer[jx][jy] = 0;
14786
14787   if (player->is_moving)
14788     TEST_DrawLevelField(player->last_jx, player->last_jy);
14789
14790   for (i = 0; i < MAX_PLAYERS; i++)
14791     if (stored_player[i].active)
14792       found = TRUE;
14793
14794   if (!found)
14795     AllPlayersGone = TRUE;
14796
14797   ExitX = ZX = jx;
14798   ExitY = ZY = jy;
14799 }
14800
14801 #if USE_NEW_SNAP_DELAY
14802 static void setFieldForSnapping(int x, int y, int element, int direction)
14803 {
14804   struct ElementInfo *ei = &element_info[element];
14805   int direction_bit = MV_DIR_TO_BIT(direction);
14806   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14807   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14808                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14809
14810   Feld[x][y] = EL_ELEMENT_SNAPPING;
14811   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14812
14813   ResetGfxAnimation(x, y);
14814
14815   GfxElement[x][y] = element;
14816   GfxAction[x][y] = action;
14817   GfxDir[x][y] = direction;
14818   GfxFrame[x][y] = -1;
14819 }
14820 #endif
14821
14822 /*
14823   =============================================================================
14824   checkDiagonalPushing()
14825   -----------------------------------------------------------------------------
14826   check if diagonal input device direction results in pushing of object
14827   (by checking if the alternative direction is walkable, diggable, ...)
14828   =============================================================================
14829 */
14830
14831 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14832                                     int x, int y, int real_dx, int real_dy)
14833 {
14834   int jx, jy, dx, dy, xx, yy;
14835
14836   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14837     return TRUE;
14838
14839   /* diagonal direction: check alternative direction */
14840   jx = player->jx;
14841   jy = player->jy;
14842   dx = x - jx;
14843   dy = y - jy;
14844   xx = jx + (dx == 0 ? real_dx : 0);
14845   yy = jy + (dy == 0 ? real_dy : 0);
14846
14847   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14848 }
14849
14850 /*
14851   =============================================================================
14852   DigField()
14853   -----------------------------------------------------------------------------
14854   x, y:                 field next to player (non-diagonal) to try to dig to
14855   real_dx, real_dy:     direction as read from input device (can be diagonal)
14856   =============================================================================
14857 */
14858
14859 static int DigField(struct PlayerInfo *player,
14860                     int oldx, int oldy, int x, int y,
14861                     int real_dx, int real_dy, int mode)
14862 {
14863   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14864   boolean player_was_pushing = player->is_pushing;
14865   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14866   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14867   int jx = oldx, jy = oldy;
14868   int dx = x - jx, dy = y - jy;
14869   int nextx = x + dx, nexty = y + dy;
14870   int move_direction = (dx == -1 ? MV_LEFT  :
14871                         dx == +1 ? MV_RIGHT :
14872                         dy == -1 ? MV_UP    :
14873                         dy == +1 ? MV_DOWN  : MV_NONE);
14874   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14875   int dig_side = MV_DIR_OPPOSITE(move_direction);
14876   int old_element = Feld[jx][jy];
14877 #if USE_FIXED_DONT_RUN_INTO
14878   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14879 #else
14880   int element;
14881 #endif
14882   int collect_count;
14883
14884   if (is_player)                /* function can also be called by EL_PENGUIN */
14885   {
14886     if (player->MovPos == 0)
14887     {
14888       player->is_digging = FALSE;
14889       player->is_collecting = FALSE;
14890     }
14891
14892     if (player->MovPos == 0)    /* last pushing move finished */
14893       player->is_pushing = FALSE;
14894
14895     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14896     {
14897       player->is_switching = FALSE;
14898       player->push_delay = -1;
14899
14900       return MP_NO_ACTION;
14901     }
14902   }
14903
14904 #if !USE_FIXED_DONT_RUN_INTO
14905   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14906     return MP_NO_ACTION;
14907 #endif
14908
14909   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14910     old_element = Back[jx][jy];
14911
14912   /* in case of element dropped at player position, check background */
14913   else if (Back[jx][jy] != EL_EMPTY &&
14914            game.engine_version >= VERSION_IDENT(2,2,0,0))
14915     old_element = Back[jx][jy];
14916
14917   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14918     return MP_NO_ACTION;        /* field has no opening in this direction */
14919
14920   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14921     return MP_NO_ACTION;        /* field has no opening in this direction */
14922
14923 #if USE_FIXED_DONT_RUN_INTO
14924   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14925   {
14926     SplashAcid(x, y);
14927
14928     Feld[jx][jy] = player->artwork_element;
14929     InitMovingField(jx, jy, MV_DOWN);
14930     Store[jx][jy] = EL_ACID;
14931     ContinueMoving(jx, jy);
14932     BuryPlayer(player);
14933
14934     return MP_DONT_RUN_INTO;
14935   }
14936 #endif
14937
14938 #if USE_FIXED_DONT_RUN_INTO
14939   if (player_can_move && DONT_RUN_INTO(element))
14940   {
14941     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14942
14943     return MP_DONT_RUN_INTO;
14944   }
14945 #endif
14946
14947 #if USE_FIXED_DONT_RUN_INTO
14948   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14949     return MP_NO_ACTION;
14950 #endif
14951
14952 #if !USE_FIXED_DONT_RUN_INTO
14953   element = Feld[x][y];
14954 #endif
14955
14956   collect_count = element_info[element].collect_count_initial;
14957
14958   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14959     return MP_NO_ACTION;
14960
14961   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14962     player_can_move = player_can_move_or_snap;
14963
14964   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14965       game.engine_version >= VERSION_IDENT(2,2,0,0))
14966   {
14967     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14968                                player->index_bit, dig_side);
14969     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14970                                         player->index_bit, dig_side);
14971
14972     if (element == EL_DC_LANDMINE)
14973       Bang(x, y);
14974
14975     if (Feld[x][y] != element)          /* field changed by snapping */
14976       return MP_ACTION;
14977
14978     return MP_NO_ACTION;
14979   }
14980
14981 #if USE_PLAYER_GRAVITY
14982   if (player->gravity && is_player && !player->is_auto_moving &&
14983       canFallDown(player) && move_direction != MV_DOWN &&
14984       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14985     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14986 #else
14987   if (game.gravity && is_player && !player->is_auto_moving &&
14988       canFallDown(player) && move_direction != MV_DOWN &&
14989       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14990     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14991 #endif
14992
14993   if (player_can_move &&
14994       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14995   {
14996     int sound_element = SND_ELEMENT(element);
14997     int sound_action = ACTION_WALKING;
14998
14999     if (IS_RND_GATE(element))
15000     {
15001       if (!player->key[RND_GATE_NR(element)])
15002         return MP_NO_ACTION;
15003     }
15004     else if (IS_RND_GATE_GRAY(element))
15005     {
15006       if (!player->key[RND_GATE_GRAY_NR(element)])
15007         return MP_NO_ACTION;
15008     }
15009     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15010     {
15011       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15012         return MP_NO_ACTION;
15013     }
15014     else if (element == EL_EXIT_OPEN ||
15015              element == EL_EM_EXIT_OPEN ||
15016 #if 1
15017              element == EL_EM_EXIT_OPENING ||
15018 #endif
15019              element == EL_STEEL_EXIT_OPEN ||
15020              element == EL_EM_STEEL_EXIT_OPEN ||
15021 #if 1
15022              element == EL_EM_STEEL_EXIT_OPENING ||
15023 #endif
15024              element == EL_SP_EXIT_OPEN ||
15025              element == EL_SP_EXIT_OPENING)
15026     {
15027       sound_action = ACTION_PASSING;    /* player is passing exit */
15028     }
15029     else if (element == EL_EMPTY)
15030     {
15031       sound_action = ACTION_MOVING;             /* nothing to walk on */
15032     }
15033
15034     /* play sound from background or player, whatever is available */
15035     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15036       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15037     else
15038       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15039   }
15040   else if (player_can_move &&
15041            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15042   {
15043     if (!ACCESS_FROM(element, opposite_direction))
15044       return MP_NO_ACTION;      /* field not accessible from this direction */
15045
15046     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15047       return MP_NO_ACTION;
15048
15049     if (IS_EM_GATE(element))
15050     {
15051       if (!player->key[EM_GATE_NR(element)])
15052         return MP_NO_ACTION;
15053     }
15054     else if (IS_EM_GATE_GRAY(element))
15055     {
15056       if (!player->key[EM_GATE_GRAY_NR(element)])
15057         return MP_NO_ACTION;
15058     }
15059     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15060     {
15061       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15062         return MP_NO_ACTION;
15063     }
15064     else if (IS_EMC_GATE(element))
15065     {
15066       if (!player->key[EMC_GATE_NR(element)])
15067         return MP_NO_ACTION;
15068     }
15069     else if (IS_EMC_GATE_GRAY(element))
15070     {
15071       if (!player->key[EMC_GATE_GRAY_NR(element)])
15072         return MP_NO_ACTION;
15073     }
15074     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15075     {
15076       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15077         return MP_NO_ACTION;
15078     }
15079     else if (element == EL_DC_GATE_WHITE ||
15080              element == EL_DC_GATE_WHITE_GRAY ||
15081              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15082     {
15083       if (player->num_white_keys == 0)
15084         return MP_NO_ACTION;
15085
15086       player->num_white_keys--;
15087     }
15088     else if (IS_SP_PORT(element))
15089     {
15090       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15091           element == EL_SP_GRAVITY_PORT_RIGHT ||
15092           element == EL_SP_GRAVITY_PORT_UP ||
15093           element == EL_SP_GRAVITY_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095         player->gravity = !player->gravity;
15096 #else
15097         game.gravity = !game.gravity;
15098 #endif
15099       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15100                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15101                element == EL_SP_GRAVITY_ON_PORT_UP ||
15102                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15103 #if USE_PLAYER_GRAVITY
15104         player->gravity = TRUE;
15105 #else
15106         game.gravity = TRUE;
15107 #endif
15108       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15109                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15110                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15111                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15112 #if USE_PLAYER_GRAVITY
15113         player->gravity = FALSE;
15114 #else
15115         game.gravity = FALSE;
15116 #endif
15117     }
15118
15119     /* automatically move to the next field with double speed */
15120     player->programmed_action = move_direction;
15121
15122     if (player->move_delay_reset_counter == 0)
15123     {
15124       player->move_delay_reset_counter = 2;     /* two double speed steps */
15125
15126       DOUBLE_PLAYER_SPEED(player);
15127     }
15128
15129     PlayLevelSoundAction(x, y, ACTION_PASSING);
15130   }
15131   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15132   {
15133     RemoveField(x, y);
15134
15135     if (mode != DF_SNAP)
15136     {
15137       GfxElement[x][y] = GFX_ELEMENT(element);
15138       player->is_digging = TRUE;
15139     }
15140
15141     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15142
15143     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15144                                         player->index_bit, dig_side);
15145
15146     if (mode == DF_SNAP)
15147     {
15148 #if USE_NEW_SNAP_DELAY
15149       if (level.block_snap_field)
15150         setFieldForSnapping(x, y, element, move_direction);
15151       else
15152         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15153 #else
15154       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15155 #endif
15156
15157       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15158                                           player->index_bit, dig_side);
15159     }
15160   }
15161   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15162   {
15163     RemoveField(x, y);
15164
15165     if (is_player && mode != DF_SNAP)
15166     {
15167       GfxElement[x][y] = element;
15168       player->is_collecting = TRUE;
15169     }
15170
15171     if (element == EL_SPEED_PILL)
15172     {
15173       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15174     }
15175     else if (element == EL_EXTRA_TIME && level.time > 0)
15176     {
15177       TimeLeft += level.extra_time;
15178
15179 #if 1
15180       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15181
15182       DisplayGameControlValues();
15183 #else
15184       DrawGameValue_Time(TimeLeft);
15185 #endif
15186     }
15187     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15188     {
15189       player->shield_normal_time_left += level.shield_normal_time;
15190       if (element == EL_SHIELD_DEADLY)
15191         player->shield_deadly_time_left += level.shield_deadly_time;
15192     }
15193     else if (element == EL_DYNAMITE ||
15194              element == EL_EM_DYNAMITE ||
15195              element == EL_SP_DISK_RED)
15196     {
15197       if (player->inventory_size < MAX_INVENTORY_SIZE)
15198         player->inventory_element[player->inventory_size++] = element;
15199
15200       DrawGameDoorValues();
15201     }
15202     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15203     {
15204       player->dynabomb_count++;
15205       player->dynabombs_left++;
15206     }
15207     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15208     {
15209       player->dynabomb_size++;
15210     }
15211     else if (element == EL_DYNABOMB_INCREASE_POWER)
15212     {
15213       player->dynabomb_xl = TRUE;
15214     }
15215     else if (IS_KEY(element))
15216     {
15217       player->key[KEY_NR(element)] = TRUE;
15218
15219       DrawGameDoorValues();
15220     }
15221     else if (element == EL_DC_KEY_WHITE)
15222     {
15223       player->num_white_keys++;
15224
15225       /* display white keys? */
15226       /* DrawGameDoorValues(); */
15227     }
15228     else if (IS_ENVELOPE(element))
15229     {
15230       player->show_envelope = element;
15231     }
15232     else if (element == EL_EMC_LENSES)
15233     {
15234       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15235
15236       RedrawAllInvisibleElementsForLenses();
15237     }
15238     else if (element == EL_EMC_MAGNIFIER)
15239     {
15240       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15241
15242       RedrawAllInvisibleElementsForMagnifier();
15243     }
15244     else if (IS_DROPPABLE(element) ||
15245              IS_THROWABLE(element))     /* can be collected and dropped */
15246     {
15247       int i;
15248
15249       if (collect_count == 0)
15250         player->inventory_infinite_element = element;
15251       else
15252         for (i = 0; i < collect_count; i++)
15253           if (player->inventory_size < MAX_INVENTORY_SIZE)
15254             player->inventory_element[player->inventory_size++] = element;
15255
15256       DrawGameDoorValues();
15257     }
15258     else if (collect_count > 0)
15259     {
15260       local_player->gems_still_needed -= collect_count;
15261       if (local_player->gems_still_needed < 0)
15262         local_player->gems_still_needed = 0;
15263
15264 #if 1
15265       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15266
15267       DisplayGameControlValues();
15268 #else
15269       DrawGameValue_Emeralds(local_player->gems_still_needed);
15270 #endif
15271     }
15272
15273     RaiseScoreElement(element);
15274     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15275
15276     if (is_player)
15277       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15278                                           player->index_bit, dig_side);
15279
15280     if (mode == DF_SNAP)
15281     {
15282 #if USE_NEW_SNAP_DELAY
15283       if (level.block_snap_field)
15284         setFieldForSnapping(x, y, element, move_direction);
15285       else
15286         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15287 #else
15288       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15289 #endif
15290
15291       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15292                                           player->index_bit, dig_side);
15293     }
15294   }
15295   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15296   {
15297     if (mode == DF_SNAP && element != EL_BD_ROCK)
15298       return MP_NO_ACTION;
15299
15300     if (CAN_FALL(element) && dy)
15301       return MP_NO_ACTION;
15302
15303     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15304         !(element == EL_SPRING && level.use_spring_bug))
15305       return MP_NO_ACTION;
15306
15307     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15308         ((move_direction & MV_VERTICAL &&
15309           ((element_info[element].move_pattern & MV_LEFT &&
15310             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15311            (element_info[element].move_pattern & MV_RIGHT &&
15312             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15313          (move_direction & MV_HORIZONTAL &&
15314           ((element_info[element].move_pattern & MV_UP &&
15315             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15316            (element_info[element].move_pattern & MV_DOWN &&
15317             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15318       return MP_NO_ACTION;
15319
15320     /* do not push elements already moving away faster than player */
15321     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15322         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15323       return MP_NO_ACTION;
15324
15325     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15326     {
15327       if (player->push_delay_value == -1 || !player_was_pushing)
15328         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15329     }
15330     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15331     {
15332       if (player->push_delay_value == -1)
15333         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15334     }
15335     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15336     {
15337       if (!player->is_pushing)
15338         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15339     }
15340
15341     player->is_pushing = TRUE;
15342     player->is_active = TRUE;
15343
15344     if (!(IN_LEV_FIELD(nextx, nexty) &&
15345           (IS_FREE(nextx, nexty) ||
15346            (IS_SB_ELEMENT(element) &&
15347             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15348            (IS_CUSTOM_ELEMENT(element) &&
15349             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15350       return MP_NO_ACTION;
15351
15352     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15353       return MP_NO_ACTION;
15354
15355     if (player->push_delay == -1)       /* new pushing; restart delay */
15356       player->push_delay = 0;
15357
15358     if (player->push_delay < player->push_delay_value &&
15359         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15360         element != EL_SPRING && element != EL_BALLOON)
15361     {
15362       /* make sure that there is no move delay before next try to push */
15363       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15364         player->move_delay = 0;
15365
15366       return MP_NO_ACTION;
15367     }
15368
15369     if (IS_CUSTOM_ELEMENT(element) &&
15370         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15371     {
15372       if (!DigFieldByCE(nextx, nexty, element))
15373         return MP_NO_ACTION;
15374     }
15375
15376     if (IS_SB_ELEMENT(element))
15377     {
15378       if (element == EL_SOKOBAN_FIELD_FULL)
15379       {
15380         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15381         local_player->sokobanfields_still_needed++;
15382       }
15383
15384       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15385       {
15386         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15387         local_player->sokobanfields_still_needed--;
15388       }
15389
15390       Feld[x][y] = EL_SOKOBAN_OBJECT;
15391
15392       if (Back[x][y] == Back[nextx][nexty])
15393         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15394       else if (Back[x][y] != 0)
15395         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15396                                     ACTION_EMPTYING);
15397       else
15398         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15399                                     ACTION_FILLING);
15400
15401 #if 1
15402       if (local_player->sokobanfields_still_needed == 0 &&
15403           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15404 #else
15405       if (local_player->sokobanfields_still_needed == 0 &&
15406           game.emulation == EMU_SOKOBAN)
15407 #endif
15408       {
15409         PlayerWins(player);
15410
15411         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15412       }
15413     }
15414     else
15415       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15416
15417     InitMovingField(x, y, move_direction);
15418     GfxAction[x][y] = ACTION_PUSHING;
15419
15420     if (mode == DF_SNAP)
15421       ContinueMoving(x, y);
15422     else
15423       MovPos[x][y] = (dx != 0 ? dx : dy);
15424
15425     Pushed[x][y] = TRUE;
15426     Pushed[nextx][nexty] = TRUE;
15427
15428     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15429       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15430     else
15431       player->push_delay_value = -1;    /* get new value later */
15432
15433     /* check for element change _after_ element has been pushed */
15434     if (game.use_change_when_pushing_bug)
15435     {
15436       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15437                                  player->index_bit, dig_side);
15438       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15439                                           player->index_bit, dig_side);
15440     }
15441   }
15442   else if (IS_SWITCHABLE(element))
15443   {
15444     if (PLAYER_SWITCHING(player, x, y))
15445     {
15446       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15447                                           player->index_bit, dig_side);
15448
15449       return MP_ACTION;
15450     }
15451
15452     player->is_switching = TRUE;
15453     player->switch_x = x;
15454     player->switch_y = y;
15455
15456     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15457
15458     if (element == EL_ROBOT_WHEEL)
15459     {
15460       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15461       ZX = x;
15462       ZY = y;
15463
15464       game.robot_wheel_active = TRUE;
15465
15466       TEST_DrawLevelField(x, y);
15467     }
15468     else if (element == EL_SP_TERMINAL)
15469     {
15470       int xx, yy;
15471
15472       SCAN_PLAYFIELD(xx, yy)
15473       {
15474         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15475           Bang(xx, yy);
15476         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15477           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15478       }
15479     }
15480     else if (IS_BELT_SWITCH(element))
15481     {
15482       ToggleBeltSwitch(x, y);
15483     }
15484     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15485              element == EL_SWITCHGATE_SWITCH_DOWN ||
15486              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15487              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15488     {
15489       ToggleSwitchgateSwitch(x, y);
15490     }
15491     else if (element == EL_LIGHT_SWITCH ||
15492              element == EL_LIGHT_SWITCH_ACTIVE)
15493     {
15494       ToggleLightSwitch(x, y);
15495     }
15496     else if (element == EL_TIMEGATE_SWITCH ||
15497              element == EL_DC_TIMEGATE_SWITCH)
15498     {
15499       ActivateTimegateSwitch(x, y);
15500     }
15501     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15502              element == EL_BALLOON_SWITCH_RIGHT ||
15503              element == EL_BALLOON_SWITCH_UP    ||
15504              element == EL_BALLOON_SWITCH_DOWN  ||
15505              element == EL_BALLOON_SWITCH_NONE  ||
15506              element == EL_BALLOON_SWITCH_ANY)
15507     {
15508       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15509                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15510                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15511                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15512                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15513                              move_direction);
15514     }
15515     else if (element == EL_LAMP)
15516     {
15517       Feld[x][y] = EL_LAMP_ACTIVE;
15518       local_player->lights_still_needed--;
15519
15520       ResetGfxAnimation(x, y);
15521       TEST_DrawLevelField(x, y);
15522     }
15523     else if (element == EL_TIME_ORB_FULL)
15524     {
15525       Feld[x][y] = EL_TIME_ORB_EMPTY;
15526
15527       if (level.time > 0 || level.use_time_orb_bug)
15528       {
15529         TimeLeft += level.time_orb_time;
15530
15531 #if 1
15532         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15533
15534         DisplayGameControlValues();
15535 #else
15536         DrawGameValue_Time(TimeLeft);
15537 #endif
15538       }
15539
15540       ResetGfxAnimation(x, y);
15541       TEST_DrawLevelField(x, y);
15542     }
15543     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15544              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15545     {
15546       int xx, yy;
15547
15548       game.ball_state = !game.ball_state;
15549
15550       SCAN_PLAYFIELD(xx, yy)
15551       {
15552         int e = Feld[xx][yy];
15553
15554         if (game.ball_state)
15555         {
15556           if (e == EL_EMC_MAGIC_BALL)
15557             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15558           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15559             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15560         }
15561         else
15562         {
15563           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15564             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15565           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15566             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15567         }
15568       }
15569     }
15570
15571     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15572                                         player->index_bit, dig_side);
15573
15574     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15575                                         player->index_bit, dig_side);
15576
15577     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15578                                         player->index_bit, dig_side);
15579
15580     return MP_ACTION;
15581   }
15582   else
15583   {
15584     if (!PLAYER_SWITCHING(player, x, y))
15585     {
15586       player->is_switching = TRUE;
15587       player->switch_x = x;
15588       player->switch_y = y;
15589
15590       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15591                                  player->index_bit, dig_side);
15592       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15593                                           player->index_bit, dig_side);
15594
15595       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15596                                  player->index_bit, dig_side);
15597       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15598                                           player->index_bit, dig_side);
15599     }
15600
15601     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15602                                player->index_bit, dig_side);
15603     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15604                                         player->index_bit, dig_side);
15605
15606     return MP_NO_ACTION;
15607   }
15608
15609   player->push_delay = -1;
15610
15611   if (is_player)                /* function can also be called by EL_PENGUIN */
15612   {
15613     if (Feld[x][y] != element)          /* really digged/collected something */
15614     {
15615       player->is_collecting = !player->is_digging;
15616       player->is_active = TRUE;
15617     }
15618   }
15619
15620   return MP_MOVING;
15621 }
15622
15623 static boolean DigFieldByCE(int x, int y, int digging_element)
15624 {
15625   int element = Feld[x][y];
15626
15627   if (!IS_FREE(x, y))
15628   {
15629     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15630                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15631                   ACTION_BREAKING);
15632
15633     /* no element can dig solid indestructible elements */
15634     if (IS_INDESTRUCTIBLE(element) &&
15635         !IS_DIGGABLE(element) &&
15636         !IS_COLLECTIBLE(element))
15637       return FALSE;
15638
15639     if (AmoebaNr[x][y] &&
15640         (element == EL_AMOEBA_FULL ||
15641          element == EL_BD_AMOEBA ||
15642          element == EL_AMOEBA_GROWING))
15643     {
15644       AmoebaCnt[AmoebaNr[x][y]]--;
15645       AmoebaCnt2[AmoebaNr[x][y]]--;
15646     }
15647
15648     if (IS_MOVING(x, y))
15649       RemoveMovingField(x, y);
15650     else
15651     {
15652       RemoveField(x, y);
15653       TEST_DrawLevelField(x, y);
15654     }
15655
15656     /* if digged element was about to explode, prevent the explosion */
15657     ExplodeField[x][y] = EX_TYPE_NONE;
15658
15659     PlayLevelSoundAction(x, y, action);
15660   }
15661
15662   Store[x][y] = EL_EMPTY;
15663
15664 #if 1
15665   /* this makes it possible to leave the removed element again */
15666   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15667     Store[x][y] = element;
15668 #else
15669   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15670   {
15671     int move_leave_element = element_info[digging_element].move_leave_element;
15672
15673     /* this makes it possible to leave the removed element again */
15674     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15675                    element : move_leave_element);
15676   }
15677 #endif
15678
15679   return TRUE;
15680 }
15681
15682 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15683 {
15684   int jx = player->jx, jy = player->jy;
15685   int x = jx + dx, y = jy + dy;
15686   int snap_direction = (dx == -1 ? MV_LEFT  :
15687                         dx == +1 ? MV_RIGHT :
15688                         dy == -1 ? MV_UP    :
15689                         dy == +1 ? MV_DOWN  : MV_NONE);
15690   boolean can_continue_snapping = (level.continuous_snapping &&
15691                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15692
15693   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15694     return FALSE;
15695
15696   if (!player->active || !IN_LEV_FIELD(x, y))
15697     return FALSE;
15698
15699   if (dx && dy)
15700     return FALSE;
15701
15702   if (!dx && !dy)
15703   {
15704     if (player->MovPos == 0)
15705       player->is_pushing = FALSE;
15706
15707     player->is_snapping = FALSE;
15708
15709     if (player->MovPos == 0)
15710     {
15711       player->is_moving = FALSE;
15712       player->is_digging = FALSE;
15713       player->is_collecting = FALSE;
15714     }
15715
15716     return FALSE;
15717   }
15718
15719 #if USE_NEW_CONTINUOUS_SNAPPING
15720   /* prevent snapping with already pressed snap key when not allowed */
15721   if (player->is_snapping && !can_continue_snapping)
15722     return FALSE;
15723 #else
15724   if (player->is_snapping)
15725     return FALSE;
15726 #endif
15727
15728   player->MovDir = snap_direction;
15729
15730   if (player->MovPos == 0)
15731   {
15732     player->is_moving = FALSE;
15733     player->is_digging = FALSE;
15734     player->is_collecting = FALSE;
15735   }
15736
15737   player->is_dropping = FALSE;
15738   player->is_dropping_pressed = FALSE;
15739   player->drop_pressed_delay = 0;
15740
15741   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15742     return FALSE;
15743
15744   player->is_snapping = TRUE;
15745   player->is_active = TRUE;
15746
15747   if (player->MovPos == 0)
15748   {
15749     player->is_moving = FALSE;
15750     player->is_digging = FALSE;
15751     player->is_collecting = FALSE;
15752   }
15753
15754   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15755     TEST_DrawLevelField(player->last_jx, player->last_jy);
15756
15757   TEST_DrawLevelField(x, y);
15758
15759   return TRUE;
15760 }
15761
15762 static boolean DropElement(struct PlayerInfo *player)
15763 {
15764   int old_element, new_element;
15765   int dropx = player->jx, dropy = player->jy;
15766   int drop_direction = player->MovDir;
15767   int drop_side = drop_direction;
15768 #if 1
15769   int drop_element = get_next_dropped_element(player);
15770 #else
15771   int drop_element = (player->inventory_size > 0 ?
15772                       player->inventory_element[player->inventory_size - 1] :
15773                       player->inventory_infinite_element != EL_UNDEFINED ?
15774                       player->inventory_infinite_element :
15775                       player->dynabombs_left > 0 ?
15776                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15777                       EL_UNDEFINED);
15778 #endif
15779
15780   player->is_dropping_pressed = TRUE;
15781
15782   /* do not drop an element on top of another element; when holding drop key
15783      pressed without moving, dropped element must move away before the next
15784      element can be dropped (this is especially important if the next element
15785      is dynamite, which can be placed on background for historical reasons) */
15786   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15787     return MP_ACTION;
15788
15789   if (IS_THROWABLE(drop_element))
15790   {
15791     dropx += GET_DX_FROM_DIR(drop_direction);
15792     dropy += GET_DY_FROM_DIR(drop_direction);
15793
15794     if (!IN_LEV_FIELD(dropx, dropy))
15795       return FALSE;
15796   }
15797
15798   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15799   new_element = drop_element;           /* default: no change when dropping */
15800
15801   /* check if player is active, not moving and ready to drop */
15802   if (!player->active || player->MovPos || player->drop_delay > 0)
15803     return FALSE;
15804
15805   /* check if player has anything that can be dropped */
15806   if (new_element == EL_UNDEFINED)
15807     return FALSE;
15808
15809   /* check if drop key was pressed long enough for EM style dynamite */
15810   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15811     return FALSE;
15812
15813   /* check if anything can be dropped at the current position */
15814   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15815     return FALSE;
15816
15817   /* collected custom elements can only be dropped on empty fields */
15818   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15819     return FALSE;
15820
15821   if (old_element != EL_EMPTY)
15822     Back[dropx][dropy] = old_element;   /* store old element on this field */
15823
15824   ResetGfxAnimation(dropx, dropy);
15825   ResetRandomAnimationValue(dropx, dropy);
15826
15827   if (player->inventory_size > 0 ||
15828       player->inventory_infinite_element != EL_UNDEFINED)
15829   {
15830     if (player->inventory_size > 0)
15831     {
15832       player->inventory_size--;
15833
15834       DrawGameDoorValues();
15835
15836       if (new_element == EL_DYNAMITE)
15837         new_element = EL_DYNAMITE_ACTIVE;
15838       else if (new_element == EL_EM_DYNAMITE)
15839         new_element = EL_EM_DYNAMITE_ACTIVE;
15840       else if (new_element == EL_SP_DISK_RED)
15841         new_element = EL_SP_DISK_RED_ACTIVE;
15842     }
15843
15844     Feld[dropx][dropy] = new_element;
15845
15846     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15847       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15848                           el2img(Feld[dropx][dropy]), 0);
15849
15850     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15851
15852     /* needed if previous element just changed to "empty" in the last frame */
15853     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15854
15855     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15856                                player->index_bit, drop_side);
15857     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15858                                         CE_PLAYER_DROPS_X,
15859                                         player->index_bit, drop_side);
15860
15861     TestIfElementTouchesCustomElement(dropx, dropy);
15862   }
15863   else          /* player is dropping a dyna bomb */
15864   {
15865     player->dynabombs_left--;
15866
15867     Feld[dropx][dropy] = new_element;
15868
15869     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15870       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15871                           el2img(Feld[dropx][dropy]), 0);
15872
15873     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15874   }
15875
15876   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15877     InitField_WithBug1(dropx, dropy, FALSE);
15878
15879   new_element = Feld[dropx][dropy];     /* element might have changed */
15880
15881   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15882       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15883   {
15884     int move_direction, nextx, nexty;
15885
15886     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15887       MovDir[dropx][dropy] = drop_direction;
15888
15889     move_direction = MovDir[dropx][dropy];
15890     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15891     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15892
15893     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15894
15895 #if USE_FIX_IMPACT_COLLISION
15896     /* do not cause impact style collision by dropping elements that can fall */
15897     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15898 #else
15899     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15900 #endif
15901   }
15902
15903   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15904   player->is_dropping = TRUE;
15905
15906   player->drop_pressed_delay = 0;
15907   player->is_dropping_pressed = FALSE;
15908
15909   player->drop_x = dropx;
15910   player->drop_y = dropy;
15911
15912   return TRUE;
15913 }
15914
15915 /* ------------------------------------------------------------------------- */
15916 /* game sound playing functions                                              */
15917 /* ------------------------------------------------------------------------- */
15918
15919 static int *loop_sound_frame = NULL;
15920 static int *loop_sound_volume = NULL;
15921
15922 void InitPlayLevelSound()
15923 {
15924   int num_sounds = getSoundListSize();
15925
15926   checked_free(loop_sound_frame);
15927   checked_free(loop_sound_volume);
15928
15929   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15930   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15931 }
15932
15933 static void PlayLevelSound(int x, int y, int nr)
15934 {
15935   int sx = SCREENX(x), sy = SCREENY(y);
15936   int volume, stereo_position;
15937   int max_distance = 8;
15938   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15939
15940   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15941       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15942     return;
15943
15944   if (!IN_LEV_FIELD(x, y) ||
15945       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15946       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15947     return;
15948
15949   volume = SOUND_MAX_VOLUME;
15950
15951   if (!IN_SCR_FIELD(sx, sy))
15952   {
15953     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15954     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15955
15956     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15957   }
15958
15959   stereo_position = (SOUND_MAX_LEFT +
15960                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15961                      (SCR_FIELDX + 2 * max_distance));
15962
15963   if (IS_LOOP_SOUND(nr))
15964   {
15965     /* This assures that quieter loop sounds do not overwrite louder ones,
15966        while restarting sound volume comparison with each new game frame. */
15967
15968     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15969       return;
15970
15971     loop_sound_volume[nr] = volume;
15972     loop_sound_frame[nr] = FrameCounter;
15973   }
15974
15975   PlaySoundExt(nr, volume, stereo_position, type);
15976 }
15977
15978 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15979 {
15980   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15981                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15982                  y < LEVELY(BY1) ? LEVELY(BY1) :
15983                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15984                  sound_action);
15985 }
15986
15987 static void PlayLevelSoundAction(int x, int y, int action)
15988 {
15989   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15990 }
15991
15992 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15993 {
15994   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15995
15996   if (sound_effect != SND_UNDEFINED)
15997     PlayLevelSound(x, y, sound_effect);
15998 }
15999
16000 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16001                                               int action)
16002 {
16003   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16004
16005   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16006     PlayLevelSound(x, y, sound_effect);
16007 }
16008
16009 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16010 {
16011   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16012
16013   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16014     PlayLevelSound(x, y, sound_effect);
16015 }
16016
16017 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16018 {
16019   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16020
16021   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16022     StopSound(sound_effect);
16023 }
16024
16025 static void PlayLevelMusic()
16026 {
16027   if (levelset.music[level_nr] != MUS_UNDEFINED)
16028     PlayMusic(levelset.music[level_nr]);        /* from config file */
16029   else
16030     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16031 }
16032
16033 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16034 {
16035   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16036   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16037   int x = xx - 1 - offset;
16038   int y = yy - 1 - offset;
16039
16040   switch (sample)
16041   {
16042     case SAMPLE_blank:
16043       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16044       break;
16045
16046     case SAMPLE_roll:
16047       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16048       break;
16049
16050     case SAMPLE_stone:
16051       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16052       break;
16053
16054     case SAMPLE_nut:
16055       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16056       break;
16057
16058     case SAMPLE_crack:
16059       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16060       break;
16061
16062     case SAMPLE_bug:
16063       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16064       break;
16065
16066     case SAMPLE_tank:
16067       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16068       break;
16069
16070     case SAMPLE_android_clone:
16071       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16072       break;
16073
16074     case SAMPLE_android_move:
16075       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16076       break;
16077
16078     case SAMPLE_spring:
16079       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16080       break;
16081
16082     case SAMPLE_slurp:
16083       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16084       break;
16085
16086     case SAMPLE_eater:
16087       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16088       break;
16089
16090     case SAMPLE_eater_eat:
16091       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16092       break;
16093
16094     case SAMPLE_alien:
16095       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16096       break;
16097
16098     case SAMPLE_collect:
16099       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16100       break;
16101
16102     case SAMPLE_diamond:
16103       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16104       break;
16105
16106     case SAMPLE_squash:
16107       /* !!! CHECK THIS !!! */
16108 #if 1
16109       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16110 #else
16111       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16112 #endif
16113       break;
16114
16115     case SAMPLE_wonderfall:
16116       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16117       break;
16118
16119     case SAMPLE_drip:
16120       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16121       break;
16122
16123     case SAMPLE_push:
16124       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16125       break;
16126
16127     case SAMPLE_dirt:
16128       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16129       break;
16130
16131     case SAMPLE_acid:
16132       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16133       break;
16134
16135     case SAMPLE_ball:
16136       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16137       break;
16138
16139     case SAMPLE_grow:
16140       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16141       break;
16142
16143     case SAMPLE_wonder:
16144       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16145       break;
16146
16147     case SAMPLE_door:
16148       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16149       break;
16150
16151     case SAMPLE_exit_open:
16152       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16153       break;
16154
16155     case SAMPLE_exit_leave:
16156       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16157       break;
16158
16159     case SAMPLE_dynamite:
16160       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16161       break;
16162
16163     case SAMPLE_tick:
16164       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16165       break;
16166
16167     case SAMPLE_press:
16168       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16169       break;
16170
16171     case SAMPLE_wheel:
16172       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16173       break;
16174
16175     case SAMPLE_boom:
16176       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16177       break;
16178
16179     case SAMPLE_die:
16180       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16181       break;
16182
16183     case SAMPLE_time:
16184       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16185       break;
16186
16187     default:
16188       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16189       break;
16190   }
16191 }
16192
16193 #if 0
16194 void ChangeTime(int value)
16195 {
16196   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16197
16198   *time += value;
16199
16200   /* EMC game engine uses value from time counter of RND game engine */
16201   level.native_em_level->lev->time = *time;
16202
16203   DrawGameValue_Time(*time);
16204 }
16205
16206 void RaiseScore(int value)
16207 {
16208   /* EMC game engine and RND game engine have separate score counters */
16209   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16210                 &level.native_em_level->lev->score : &local_player->score);
16211
16212   *score += value;
16213
16214   DrawGameValue_Score(*score);
16215 }
16216 #endif
16217
16218 void RaiseScore(int value)
16219 {
16220   local_player->score += value;
16221
16222 #if 1
16223   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16224
16225   DisplayGameControlValues();
16226 #else
16227   DrawGameValue_Score(local_player->score);
16228 #endif
16229 }
16230
16231 void RaiseScoreElement(int element)
16232 {
16233   switch (element)
16234   {
16235     case EL_EMERALD:
16236     case EL_BD_DIAMOND:
16237     case EL_EMERALD_YELLOW:
16238     case EL_EMERALD_RED:
16239     case EL_EMERALD_PURPLE:
16240     case EL_SP_INFOTRON:
16241       RaiseScore(level.score[SC_EMERALD]);
16242       break;
16243     case EL_DIAMOND:
16244       RaiseScore(level.score[SC_DIAMOND]);
16245       break;
16246     case EL_CRYSTAL:
16247       RaiseScore(level.score[SC_CRYSTAL]);
16248       break;
16249     case EL_PEARL:
16250       RaiseScore(level.score[SC_PEARL]);
16251       break;
16252     case EL_BUG:
16253     case EL_BD_BUTTERFLY:
16254     case EL_SP_ELECTRON:
16255       RaiseScore(level.score[SC_BUG]);
16256       break;
16257     case EL_SPACESHIP:
16258     case EL_BD_FIREFLY:
16259     case EL_SP_SNIKSNAK:
16260       RaiseScore(level.score[SC_SPACESHIP]);
16261       break;
16262     case EL_YAMYAM:
16263     case EL_DARK_YAMYAM:
16264       RaiseScore(level.score[SC_YAMYAM]);
16265       break;
16266     case EL_ROBOT:
16267       RaiseScore(level.score[SC_ROBOT]);
16268       break;
16269     case EL_PACMAN:
16270       RaiseScore(level.score[SC_PACMAN]);
16271       break;
16272     case EL_NUT:
16273       RaiseScore(level.score[SC_NUT]);
16274       break;
16275     case EL_DYNAMITE:
16276     case EL_EM_DYNAMITE:
16277     case EL_SP_DISK_RED:
16278     case EL_DYNABOMB_INCREASE_NUMBER:
16279     case EL_DYNABOMB_INCREASE_SIZE:
16280     case EL_DYNABOMB_INCREASE_POWER:
16281       RaiseScore(level.score[SC_DYNAMITE]);
16282       break;
16283     case EL_SHIELD_NORMAL:
16284     case EL_SHIELD_DEADLY:
16285       RaiseScore(level.score[SC_SHIELD]);
16286       break;
16287     case EL_EXTRA_TIME:
16288       RaiseScore(level.extra_time_score);
16289       break;
16290     case EL_KEY_1:
16291     case EL_KEY_2:
16292     case EL_KEY_3:
16293     case EL_KEY_4:
16294     case EL_EM_KEY_1:
16295     case EL_EM_KEY_2:
16296     case EL_EM_KEY_3:
16297     case EL_EM_KEY_4:
16298     case EL_EMC_KEY_5:
16299     case EL_EMC_KEY_6:
16300     case EL_EMC_KEY_7:
16301     case EL_EMC_KEY_8:
16302     case EL_DC_KEY_WHITE:
16303       RaiseScore(level.score[SC_KEY]);
16304       break;
16305     default:
16306       RaiseScore(element_info[element].collect_score);
16307       break;
16308   }
16309 }
16310
16311 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16312 {
16313   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16314   {
16315 #if defined(NETWORK_AVALIABLE)
16316     if (options.network)
16317       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16318     else
16319 #endif
16320     {
16321       if (quick_quit)
16322       {
16323 #if 1
16324
16325 #if 1
16326         FadeSkipNextFadeIn();
16327 #else
16328         fading = fading_none;
16329 #endif
16330
16331 #else
16332         OpenDoor(DOOR_CLOSE_1);
16333 #endif
16334
16335         game_status = GAME_MODE_MAIN;
16336
16337 #if 1
16338         DrawAndFadeInMainMenu(REDRAW_FIELD);
16339 #else
16340         DrawMainMenu();
16341 #endif
16342       }
16343       else
16344       {
16345 #if 0
16346         FadeOut(REDRAW_FIELD);
16347 #endif
16348
16349         game_status = GAME_MODE_MAIN;
16350
16351         DrawAndFadeInMainMenu(REDRAW_FIELD);
16352       }
16353     }
16354   }
16355   else          /* continue playing the game */
16356   {
16357     if (tape.playing && tape.deactivate_display)
16358       TapeDeactivateDisplayOff(TRUE);
16359
16360     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16361
16362     if (tape.playing && tape.deactivate_display)
16363       TapeDeactivateDisplayOn();
16364   }
16365 }
16366
16367 void RequestQuitGame(boolean ask_if_really_quit)
16368 {
16369   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16370   boolean skip_request = AllPlayersGone || quick_quit;
16371
16372   RequestQuitGameExt(skip_request, quick_quit,
16373                      "Do you really want to quit the game ?");
16374 }
16375
16376
16377 /* ------------------------------------------------------------------------- */
16378 /* random generator functions                                                */
16379 /* ------------------------------------------------------------------------- */
16380
16381 unsigned int InitEngineRandom_RND(long seed)
16382 {
16383   game.num_random_calls = 0;
16384
16385 #if 0
16386   unsigned int rnd_seed = InitEngineRandom(seed);
16387
16388   printf("::: START RND: %d\n", rnd_seed);
16389
16390   return rnd_seed;
16391 #else
16392
16393   return InitEngineRandom(seed);
16394
16395 #endif
16396
16397 }
16398
16399 unsigned int RND(int max)
16400 {
16401   if (max > 0)
16402   {
16403     game.num_random_calls++;
16404
16405     return GetEngineRandom(max);
16406   }
16407
16408   return 0;
16409 }
16410
16411
16412 /* ------------------------------------------------------------------------- */
16413 /* game engine snapshot handling functions                                   */
16414 /* ------------------------------------------------------------------------- */
16415
16416 struct EngineSnapshotInfo
16417 {
16418   /* runtime values for custom element collect score */
16419   int collect_score[NUM_CUSTOM_ELEMENTS];
16420
16421   /* runtime values for group element choice position */
16422   int choice_pos[NUM_GROUP_ELEMENTS];
16423
16424   /* runtime values for belt position animations */
16425   int belt_graphic[4 * NUM_BELT_PARTS];
16426   int belt_anim_mode[4 * NUM_BELT_PARTS];
16427 };
16428
16429 static struct EngineSnapshotInfo engine_snapshot_rnd;
16430 static char *snapshot_level_identifier = NULL;
16431 static int snapshot_level_nr = -1;
16432
16433 static void SaveEngineSnapshotValues_RND()
16434 {
16435   static int belt_base_active_element[4] =
16436   {
16437     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16438     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16439     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16440     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16441   };
16442   int i, j;
16443
16444   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16445   {
16446     int element = EL_CUSTOM_START + i;
16447
16448     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16449   }
16450
16451   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16452   {
16453     int element = EL_GROUP_START + i;
16454
16455     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16456   }
16457
16458   for (i = 0; i < 4; i++)
16459   {
16460     for (j = 0; j < NUM_BELT_PARTS; j++)
16461     {
16462       int element = belt_base_active_element[i] + j;
16463       int graphic = el2img(element);
16464       int anim_mode = graphic_info[graphic].anim_mode;
16465
16466       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16467       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16468     }
16469   }
16470 }
16471
16472 static void LoadEngineSnapshotValues_RND()
16473 {
16474   unsigned long num_random_calls = game.num_random_calls;
16475   int i, j;
16476
16477   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16478   {
16479     int element = EL_CUSTOM_START + i;
16480
16481     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16482   }
16483
16484   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16485   {
16486     int element = EL_GROUP_START + i;
16487
16488     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16489   }
16490
16491   for (i = 0; i < 4; i++)
16492   {
16493     for (j = 0; j < NUM_BELT_PARTS; j++)
16494     {
16495       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16496       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16497
16498       graphic_info[graphic].anim_mode = anim_mode;
16499     }
16500   }
16501
16502   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16503   {
16504     InitRND(tape.random_seed);
16505     for (i = 0; i < num_random_calls; i++)
16506       RND(1);
16507   }
16508
16509   if (game.num_random_calls != num_random_calls)
16510   {
16511     Error(ERR_INFO, "number of random calls out of sync");
16512     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16513     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16514     Error(ERR_EXIT, "this should not happen -- please debug");
16515   }
16516 }
16517
16518 void SaveEngineSnapshot()
16519 {
16520   /* do not save snapshots from editor */
16521   if (level_editor_test_game)
16522     return;
16523
16524   /* free previous snapshot buffers, if needed */
16525   FreeEngineSnapshotBuffers();
16526
16527   /* copy some special values to a structure better suited for the snapshot */
16528
16529   SaveEngineSnapshotValues_RND();
16530   SaveEngineSnapshotValues_EM();
16531   SaveEngineSnapshotValues_SP();
16532
16533   /* save values stored in special snapshot structure */
16534
16535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16538
16539   /* save further RND engine values */
16540
16541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16544
16545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16549
16550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16555
16556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16559
16560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16561
16562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16563
16564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16566
16567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16585
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16588
16589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16592
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16595
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16601
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16604
16605   /* save level identification information */
16606
16607   setString(&snapshot_level_identifier, leveldir_current->identifier);
16608   snapshot_level_nr = level_nr;
16609
16610 #if 0
16611   ListNode *node = engine_snapshot_list_rnd;
16612   int num_bytes = 0;
16613
16614   while (node != NULL)
16615   {
16616     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16617
16618     node = node->next;
16619   }
16620
16621   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16622 #endif
16623 }
16624
16625 void LoadEngineSnapshot()
16626 {
16627   /* restore generically stored snapshot buffers */
16628
16629   LoadEngineSnapshotBuffers();
16630
16631   /* restore special values from snapshot structure */
16632
16633   LoadEngineSnapshotValues_RND();
16634   LoadEngineSnapshotValues_EM();
16635   LoadEngineSnapshotValues_SP();
16636 }
16637
16638 boolean CheckEngineSnapshot()
16639 {
16640   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16641           snapshot_level_nr == level_nr);
16642 }
16643
16644
16645 /* ---------- new game button stuff ---------------------------------------- */
16646
16647 /* graphic position values for game buttons */
16648 #define GAME_BUTTON_XSIZE       30
16649 #define GAME_BUTTON_YSIZE       30
16650 #define GAME_BUTTON_XPOS        5
16651 #define GAME_BUTTON_YPOS        215
16652 #define SOUND_BUTTON_XPOS       5
16653 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16654
16655 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16656 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16657 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16658 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16659 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16660 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16661
16662 static struct
16663 {
16664   int *x, *y;
16665   int gd_x, gd_y;
16666   int gadget_id;
16667   char *infotext;
16668 } gamebutton_info[NUM_GAME_BUTTONS] =
16669 {
16670 #if 1
16671   {
16672     &game.button.stop.x,        &game.button.stop.y,
16673     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16674     GAME_CTRL_ID_STOP,
16675     "stop game"
16676   },
16677   {
16678     &game.button.pause.x,       &game.button.pause.y,
16679     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16680     GAME_CTRL_ID_PAUSE,
16681     "pause game"
16682   },
16683   {
16684     &game.button.play.x,        &game.button.play.y,
16685     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16686     GAME_CTRL_ID_PLAY,
16687     "play game"
16688   },
16689   {
16690     &game.button.sound_music.x, &game.button.sound_music.y,
16691     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16692     SOUND_CTRL_ID_MUSIC,
16693     "background music on/off"
16694   },
16695   {
16696     &game.button.sound_loops.x, &game.button.sound_loops.y,
16697     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16698     SOUND_CTRL_ID_LOOPS,
16699     "sound loops on/off"
16700   },
16701   {
16702     &game.button.sound_simple.x,&game.button.sound_simple.y,
16703     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16704     SOUND_CTRL_ID_SIMPLE,
16705     "normal sounds on/off"
16706   }
16707 #else
16708   {
16709     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16710     GAME_CTRL_ID_STOP,
16711     "stop game"
16712   },
16713   {
16714     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16715     GAME_CTRL_ID_PAUSE,
16716     "pause game"
16717   },
16718   {
16719     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16720     GAME_CTRL_ID_PLAY,
16721     "play game"
16722   },
16723   {
16724     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16725     SOUND_CTRL_ID_MUSIC,
16726     "background music on/off"
16727   },
16728   {
16729     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16730     SOUND_CTRL_ID_LOOPS,
16731     "sound loops on/off"
16732   },
16733   {
16734     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16735     SOUND_CTRL_ID_SIMPLE,
16736     "normal sounds on/off"
16737   }
16738 #endif
16739 };
16740
16741 void CreateGameButtons()
16742 {
16743   int i;
16744
16745   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16746   {
16747     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16748     struct GadgetInfo *gi;
16749     int button_type;
16750     boolean checked;
16751     unsigned long event_mask;
16752     int x, y;
16753     int gd_xoffset, gd_yoffset;
16754     int gd_x1, gd_x2, gd_y1, gd_y2;
16755     int id = i;
16756
16757     x = DX + *gamebutton_info[i].x;
16758     y = DY + *gamebutton_info[i].y;
16759     gd_xoffset = gamebutton_info[i].gd_x;
16760     gd_yoffset = gamebutton_info[i].gd_y;
16761     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16762     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16763
16764     if (id == GAME_CTRL_ID_STOP ||
16765         id == GAME_CTRL_ID_PAUSE ||
16766         id == GAME_CTRL_ID_PLAY)
16767     {
16768       button_type = GD_TYPE_NORMAL_BUTTON;
16769       checked = FALSE;
16770       event_mask = GD_EVENT_RELEASED;
16771       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16772       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16773     }
16774     else
16775     {
16776       button_type = GD_TYPE_CHECK_BUTTON;
16777       checked =
16778         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16779          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16780          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16781       event_mask = GD_EVENT_PRESSED;
16782       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16783       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16784     }
16785
16786     gi = CreateGadget(GDI_CUSTOM_ID, id,
16787                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16788 #if 1
16789                       GDI_X, x,
16790                       GDI_Y, y,
16791 #else
16792                       GDI_X, DX + gd_xoffset,
16793                       GDI_Y, DY + gd_yoffset,
16794 #endif
16795                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16796                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16797                       GDI_TYPE, button_type,
16798                       GDI_STATE, GD_BUTTON_UNPRESSED,
16799                       GDI_CHECKED, checked,
16800                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16801                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16802                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16803                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16804                       GDI_DIRECT_DRAW, FALSE,
16805                       GDI_EVENT_MASK, event_mask,
16806                       GDI_CALLBACK_ACTION, HandleGameButtons,
16807                       GDI_END);
16808
16809     if (gi == NULL)
16810       Error(ERR_EXIT, "cannot create gadget");
16811
16812     game_gadget[id] = gi;
16813   }
16814 }
16815
16816 void FreeGameButtons()
16817 {
16818   int i;
16819
16820   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16821     FreeGadget(game_gadget[i]);
16822 }
16823
16824 static void MapGameButtons()
16825 {
16826   int i;
16827
16828   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16829     MapGadget(game_gadget[i]);
16830 }
16831
16832 void UnmapGameButtons()
16833 {
16834   int i;
16835
16836   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16837     UnmapGadget(game_gadget[i]);
16838 }
16839
16840 void RedrawGameButtons()
16841 {
16842   int i;
16843
16844   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16845     RedrawGadget(game_gadget[i]);
16846 }
16847
16848 static void HandleGameButtons(struct GadgetInfo *gi)
16849 {
16850   int id = gi->custom_id;
16851
16852   if (game_status != GAME_MODE_PLAYING)
16853     return;
16854
16855   switch (id)
16856   {
16857     case GAME_CTRL_ID_STOP:
16858       if (tape.playing)
16859         TapeStop();
16860       else
16861         RequestQuitGame(TRUE);
16862       break;
16863
16864     case GAME_CTRL_ID_PAUSE:
16865       if (options.network)
16866       {
16867 #if defined(NETWORK_AVALIABLE)
16868         if (tape.pausing)
16869           SendToServer_ContinuePlaying();
16870         else
16871           SendToServer_PausePlaying();
16872 #endif
16873       }
16874       else
16875         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16876       break;
16877
16878     case GAME_CTRL_ID_PLAY:
16879       if (tape.pausing)
16880       {
16881 #if defined(NETWORK_AVALIABLE)
16882         if (options.network)
16883           SendToServer_ContinuePlaying();
16884         else
16885 #endif
16886         {
16887           tape.pausing = FALSE;
16888           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16889         }
16890       }
16891       break;
16892
16893     case SOUND_CTRL_ID_MUSIC:
16894       if (setup.sound_music)
16895       { 
16896         setup.sound_music = FALSE;
16897         FadeMusic();
16898       }
16899       else if (audio.music_available)
16900       { 
16901         setup.sound = setup.sound_music = TRUE;
16902
16903         SetAudioMode(setup.sound);
16904
16905         PlayLevelMusic();
16906       }
16907       break;
16908
16909     case SOUND_CTRL_ID_LOOPS:
16910       if (setup.sound_loops)
16911         setup.sound_loops = FALSE;
16912       else if (audio.loops_available)
16913       {
16914         setup.sound = setup.sound_loops = TRUE;
16915         SetAudioMode(setup.sound);
16916       }
16917       break;
16918
16919     case SOUND_CTRL_ID_SIMPLE:
16920       if (setup.sound_simple)
16921         setup.sound_simple = FALSE;
16922       else if (audio.sound_available)
16923       {
16924         setup.sound = setup.sound_simple = TRUE;
16925         SetAudioMode(setup.sound);
16926       }
16927       break;
16928
16929     default:
16930       break;
16931   }
16932 }